SCPM API
For PR and default-branch scanning, see scan coverage, start operations and completion checks.
The /scpm API manages supply-chain findings and package policy. See Supply Chain Posture Management for the difference between package hygiene, suspicious behavior, and confirmed threats.
Base URL
https://api.<TENANT>.nullify.ai
Conventions
Provider scope. Calls are scoped to one source-control account. Pass the query parameter for your provider:
githubOwnerId,gitlabGroupId,azureOrganizationId,bitbucketWorkspaceId, orinstallationId.Permissions. Findings reads need finding read or list access, finding changes (update, allowlist, tickets, autofix) need finding write access, and settings and malware analysis need configuration read or write access. Missing access returns 403.
Finding IDs.
{findingId}comes from the finding list calls. An unknown ID returns 404. An ID that belongs to another tenant returns 403.Pagination. Lists use
nextTokenandlimit, capped at 100 (see Pagination). Scan runs useoffsetinstead.
Findings
List findings
Lists SCPM findings as previews. Filters are priorityLabel, severity, branch, workflow, ruleId, hasPullRequest, isResolved, isFixed, isFalsePositive, isAllowlisted, isArchived, and the repeatable repositoryIds, findingType (for example known_malware or cicd_unpinned_action), language and fileOwnerName. Sort with sortBy (time or priority) and sort (asc or desc, default desc). limit defaults to and is capped at 100. Follow nextToken for more.
Returns a filtered set of SCPM findings based on query parameters
only findings that do (or do not) have a linked pull request
if not provided, all repositories will be included
only findings produced by this rule (rule source id such as scpm-cicd-unpinned-action, or the finding type)
only findings of these supply-chain types (known_malware, cicd_unpinned_action, ...)
only findings in these languages
combination of isFixed, isFalsePositive, isAllowlisted and isArchived
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
GET /scpm/findings HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"findings": [
{
"branch": "text",
"category": "text",
"commitAuthor": "text",
"commitHash": "text",
"commitMessage": "text",
"commitUrl": "text",
"confidence": "text",
"createdAt": "text",
"description": "text",
"enforcement": {
"mode": "off",
"severity": "CRITICAL",
"source": "default",
"suppressed": true,
"suppressedBy": ""
},
"falsePositiveReason": "text",
"filePath": "text",
"findingType": "known_malware",
"id": "text",
"installationId": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"isResolved": true,
"language": "text",
"lockfilePath": "text",
"manifestPath": "text",
"message": "text",
"owasp": [
"text"
],
"owner": "text",
"packageManager": "text",
"packageName": "text",
"packageVersion": "text",
"platform": "text",
"priorityLabel": "URGENT",
"priorityLabelReason": "text",
"priorityScore": 1,
"projectId": "text",
"projectName": "text",
"pullRequestId": "text",
"pullRequestUrl": "text",
"references": [
"text"
],
"repository": "text",
"repositoryId": "text",
"ruleId": "text",
"ruleName": "text",
"ruleSourceId": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"tags": [
"text"
],
"tenantId": "text",
"title": "text",
"triageEnd": "2026-01-01T00:00:00.000Z",
"updatedAt": "text"
}
],
"nextToken": "text",
"numItems": 1,
"version": "text"
}List findings (preview alias)
GET /scpm/findings/preview is a deprecated alias that runs the same handler with the same parameters. Use /scpm/findings in new integrations.
Returns a filtered set of SCPM findings based on query parameters
only findings that do (or do not) have a linked pull request
if not provided, all repositories will be included
only findings produced by this rule (rule source id such as scpm-cicd-unpinned-action, or the finding type)
only findings of these supply-chain types (known_malware, cicd_unpinned_action, ...)
only findings in these languages
combination of isFixed, isFalsePositive, isAllowlisted and isArchived
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
GET /scpm/findings/preview HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"findings": [
{
"branch": "text",
"category": "text",
"commitAuthor": "text",
"commitHash": "text",
"commitMessage": "text",
"commitUrl": "text",
"confidence": "text",
"createdAt": "text",
"description": "text",
"enforcement": {
"mode": "off",
"severity": "CRITICAL",
"source": "default",
"suppressed": true,
"suppressedBy": ""
},
"falsePositiveReason": "text",
"filePath": "text",
"findingType": "known_malware",
"id": "text",
"installationId": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"isResolved": true,
"language": "text",
"lockfilePath": "text",
"manifestPath": "text",
"message": "text",
"owasp": [
"text"
],
"owner": "text",
"packageManager": "text",
"packageName": "text",
"packageVersion": "text",
"platform": "text",
"priorityLabel": "URGENT",
"priorityLabelReason": "text",
"priorityScore": 1,
"projectId": "text",
"projectName": "text",
"pullRequestId": "text",
"pullRequestUrl": "text",
"references": [
"text"
],
"repository": "text",
"repositoryId": "text",
"ruleId": "text",
"ruleName": "text",
"ruleSourceId": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"tags": [
"text"
],
"tenantId": "text",
"title": "text",
"triageEnd": "2026-01-01T00:00:00.000Z",
"updatedAt": "text"
}
],
"nextToken": "text",
"numItems": 1,
"version": "text"
}List findings in full
Returns complete finding records instead of previews, with the same filters. The repository filter here is the single-valued repositoryId rather than repositoryIds. The gateBase parameter is used by Nullify's pull-request gate to fetch its comparison baseline, including allowlisted and false-positive rows. When it is set, the response also reports cooldownWarnOnly, and scannedBaseline if you also send repositoryId and branch. Ordinary integrations should leave it unset.
Returns current SCPM findings in default branches
only findings produced by this rule (rule source id or finding type)
only findings that do (or do not) have a linked pull request
only findings of these supply-chain types (known_malware, cicd_unpinned_action, ...)
only findings in these languages
if set, only findings for this repository are returned
combination of isFixed, isFalsePositive, isAllowlisted and isArchived
orchestrator net-new base set: current-branch latest, resolution-agnostic (includes allowlisted/false-positive rows)
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
GET /scpm/findings/detailed HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"cooldownWarnOnly": true,
"findings": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"autoFixCurrentActivity": "text",
"autoFixEndTime": "2026-01-01T00:00:00.000Z",
"autoFixStartTime": "2026-01-01T00:00:00.000Z",
"autoFixState": "none",
"autofixVerdictAt": "2026-01-01T00:00:00.000Z",
"autofixVerdictExplanation": "text",
"autofixVerdictReason": "AGENT_ERROR",
"branch": "text",
"budgetExhausted": true,
"budgetLimit": 1,
"budgetUsed": 1,
"commitAuthor": "text",
"commitHash": "text",
"commitMessage": "text",
"commitUrl": "text",
"concurrencyLimit": 1,
"cooldownExpiresAt": "2026-01-01T00:00:00.000Z",
"cooldownPublishedAt": "2026-01-01T00:00:00.000Z",
"createdAt": "text",
"deletedAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"endLine": 1,
"enforcement": {
"mode": "off",
"severity": "CRITICAL",
"source": "default",
"suppressed": true,
"suppressedBy": ""
},
"failedTriage": true,
"falsePositiveReason": "text",
"fileOwners": [
{
"name": "text",
"type": "email"
}
],
"filePath": "text",
"findingType": "known_malware",
"firstSeenCommit": "text",
"id": "text",
"installationId": "text",
"isAllowlisted": true,
"isArchived": true,
"isAutoFixableAgentVerdict": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"isResolved": true,
"language": "text",
"lockfilePath": "text",
"manifestPath": "text",
"message": "text",
"owner": "text",
"packageManager": "text",
"packageName": "text",
"packageVersion": "text",
"platform": "text",
"priorityLabel": "URGENT",
"priorityLabelReason": "text",
"priorityOverride": "URGENT",
"priorityScore": 1,
"projectId": "text",
"projectName": "text",
"pullRequestId": "text",
"pullRequestUrl": "text",
"pullRequestsAutofix": [
{
"ciCheckedAt": "2026-01-01T00:00:00.000Z",
"ciDetailUrl": "text",
"ciState": "pending",
"createdAt": "2026-01-01T00:00:00.000Z",
"explanation": "text",
"firstReviewedAt": "2026-01-01T00:00:00.000Z",
"hasCustomerCommit": true,
"hasCustomerFeedback": true,
"id": 1,
"lastReviewedAt": "2026-01-01T00:00:00.000Z",
"link": "text",
"mergedAt": "2026-01-01T00:00:00.000Z",
"pullRequestProvider": {
"azure": {
"pullRequestId": 1
},
"bitbucket": {
"pullRequestId": 1
},
"github": {
"pullRequestNodeId": "text",
"pullRequestNumber": 1
},
"gitlab": {
"mergeRequestNumber": 1
},
"id": "DX"
},
"recordId": "text",
"status": "open",
"title": "text",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"pureInsertion": true,
"repository": "text",
"repositoryId": "text",
"resolvedAt": "2026-01-01T00:00:00.000Z",
"ruleId": "text",
"ruleSourceId": "text",
"ruleUrl": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"startLine": 1,
"tenantId": "text",
"ticket": {
"asana": {
"projectGid": "text",
"taskGid": "text",
"url": "text"
},
"azure": {
"url": "text",
"workItemId": 1
},
"github": {
"issueId": 1,
"nodeId": "text",
"number": 1,
"repositoryName": "text",
"url": "text"
},
"gitlab": {},
"jira": {
"issueId": "text",
"issueKey": "text",
"url": "text"
},
"linear": {
"issueId": "text",
"teamId": "text",
"url": "text"
},
"providerId": "DX"
},
"ticketState": "text",
"title": "text",
"updatedAt": "text",
"userNotes": "text",
"workflow": "text"
}
],
"nextToken": "text",
"numItems": 1,
"scannedBaseline": true,
"version": "text"
}Get a finding
Returns the finding with its explanation and impact, a presignedUrl for the stored finding detail, and movements, the history of locations where the finding has been seen, newest first.
Returns a given finding with explanation and impact
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
GET /scpm/findings/{findingId} HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"finding": {
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"autoFixCurrentActivity": "text",
"autoFixEndTime": "2026-01-01T00:00:00.000Z",
"autoFixStartTime": "2026-01-01T00:00:00.000Z",
"autoFixState": "none",
"autofixVerdictAt": "2026-01-01T00:00:00.000Z",
"autofixVerdictExplanation": "text",
"autofixVerdictReason": "AGENT_ERROR",
"branch": "text",
"budgetExhausted": true,
"budgetLimit": 1,
"budgetUsed": 1,
"commitAuthor": "text",
"commitHash": "text",
"commitMessage": "text",
"commitUrl": "text",
"concurrencyLimit": 1,
"cooldownExpiresAt": "2026-01-01T00:00:00.000Z",
"cooldownPublishedAt": "2026-01-01T00:00:00.000Z",
"createdAt": "text",
"deletedAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"endLine": 1,
"enforcement": {
"mode": "off",
"severity": "CRITICAL",
"source": "default",
"suppressed": true,
"suppressedBy": ""
},
"failedTriage": true,
"falsePositiveReason": "text",
"fileOwners": [
{
"name": "text",
"type": "email"
}
],
"filePath": "text",
"findingType": "known_malware",
"firstSeenCommit": "text",
"id": "text",
"installationId": "text",
"isAllowlisted": true,
"isArchived": true,
"isAutoFixableAgentVerdict": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"isResolved": true,
"language": "text",
"lockfilePath": "text",
"manifestPath": "text",
"message": "text",
"owner": "text",
"packageManager": "text",
"packageName": "text",
"packageVersion": "text",
"platform": "text",
"priorityLabel": "URGENT",
"priorityLabelReason": "text",
"priorityOverride": "URGENT",
"priorityScore": 1,
"projectId": "text",
"projectName": "text",
"pullRequestId": "text",
"pullRequestUrl": "text",
"pullRequestsAutofix": [
{
"ciCheckedAt": "2026-01-01T00:00:00.000Z",
"ciDetailUrl": "text",
"ciState": "pending",
"createdAt": "2026-01-01T00:00:00.000Z",
"explanation": "text",
"firstReviewedAt": "2026-01-01T00:00:00.000Z",
"hasCustomerCommit": true,
"hasCustomerFeedback": true,
"id": 1,
"lastReviewedAt": "2026-01-01T00:00:00.000Z",
"link": "text",
"mergedAt": "2026-01-01T00:00:00.000Z",
"pullRequestProvider": {
"azure": {
"pullRequestId": 1
},
"bitbucket": {
"pullRequestId": 1
},
"github": {
"pullRequestNodeId": "text",
"pullRequestNumber": 1
},
"gitlab": {
"mergeRequestNumber": 1
},
"id": "DX"
},
"recordId": "text",
"status": "open",
"title": "text",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"pureInsertion": true,
"repository": "text",
"repositoryId": "text",
"resolvedAt": "2026-01-01T00:00:00.000Z",
"ruleId": "text",
"ruleSourceId": "text",
"ruleUrl": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"startLine": 1,
"tenantId": "text",
"ticket": {
"asana": {
"projectGid": "text",
"taskGid": "text",
"url": "text"
},
"azure": {
"url": "text",
"workItemId": 1
},
"github": {
"issueId": 1,
"nodeId": "text",
"number": 1,
"repositoryName": "text",
"url": "text"
},
"gitlab": {},
"jira": {
"issueId": "text",
"issueKey": "text",
"url": "text"
},
"linear": {
"issueId": "text",
"teamId": "text",
"url": "text"
},
"providerId": "DX"
},
"ticketState": "text",
"title": "text",
"updatedAt": "text",
"userNotes": "text",
"workflow": "text"
},
"movements": [
{
"commitHash": "text",
"commitTime": "text",
"endLine": 1,
"filePath": "text",
"packageVersion": "text",
"previousCommitHash": "text",
"previousFilePath": "text",
"reason": "text",
"startLine": 1
}
],
"presignedUrl": "text",
"version": "text"
}Update a finding
Sets overrides on a finding. severityOverride and priorityOverride are validated against the allowed values (400 if not), userNotes stores your free-text note, and isFalsePositive can only be set to false, which reverses a false-positive verdict and reopens the finding's ticket. Setting it to true returns 400, because a dismissal is a triage verdict with a reason; use allowlisting to suppress a finding. Omitted fields are left unchanged, and the updated finding is returned.
Update a given finding with the fields provided
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
PATCH /scpm/findings/{findingId} HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 101
{
"isFalsePositive": true,
"priorityOverride": "URGENT",
"severityOverride": "CRITICAL",
"userNotes": "text"
}{
"finding": {
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"autoFixCurrentActivity": "text",
"autoFixEndTime": "2026-01-01T00:00:00.000Z",
"autoFixStartTime": "2026-01-01T00:00:00.000Z",
"autoFixState": "none",
"autofixVerdictAt": "2026-01-01T00:00:00.000Z",
"autofixVerdictExplanation": "text",
"autofixVerdictReason": "AGENT_ERROR",
"branch": "text",
"budgetExhausted": true,
"budgetLimit": 1,
"budgetUsed": 1,
"commitAuthor": "text",
"commitHash": "text",
"commitMessage": "text",
"commitUrl": "text",
"concurrencyLimit": 1,
"cooldownExpiresAt": "2026-01-01T00:00:00.000Z",
"cooldownPublishedAt": "2026-01-01T00:00:00.000Z",
"createdAt": "text",
"deletedAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"endLine": 1,
"enforcement": {
"mode": "off",
"severity": "CRITICAL",
"source": "default",
"suppressed": true,
"suppressedBy": ""
},
"failedTriage": true,
"falsePositiveReason": "text",
"fileOwners": [
{
"name": "text",
"type": "email"
}
],
"filePath": "text",
"findingType": "known_malware",
"firstSeenCommit": "text",
"id": "text",
"installationId": "text",
"isAllowlisted": true,
"isArchived": true,
"isAutoFixableAgentVerdict": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"isResolved": true,
"language": "text",
"lockfilePath": "text",
"manifestPath": "text",
"message": "text",
"owner": "text",
"packageManager": "text",
"packageName": "text",
"packageVersion": "text",
"platform": "text",
"priorityLabel": "URGENT",
"priorityLabelReason": "text",
"priorityOverride": "URGENT",
"priorityScore": 1,
"projectId": "text",
"projectName": "text",
"pullRequestId": "text",
"pullRequestUrl": "text",
"pullRequestsAutofix": [
{
"ciCheckedAt": "2026-01-01T00:00:00.000Z",
"ciDetailUrl": "text",
"ciState": "pending",
"createdAt": "2026-01-01T00:00:00.000Z",
"explanation": "text",
"firstReviewedAt": "2026-01-01T00:00:00.000Z",
"hasCustomerCommit": true,
"hasCustomerFeedback": true,
"id": 1,
"lastReviewedAt": "2026-01-01T00:00:00.000Z",
"link": "text",
"mergedAt": "2026-01-01T00:00:00.000Z",
"pullRequestProvider": {
"azure": {
"pullRequestId": 1
},
"bitbucket": {
"pullRequestId": 1
},
"github": {
"pullRequestNodeId": "text",
"pullRequestNumber": 1
},
"gitlab": {
"mergeRequestNumber": 1
},
"id": "DX"
},
"recordId": "text",
"status": "open",
"title": "text",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"pureInsertion": true,
"repository": "text",
"repositoryId": "text",
"resolvedAt": "2026-01-01T00:00:00.000Z",
"ruleId": "text",
"ruleSourceId": "text",
"ruleUrl": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"startLine": 1,
"tenantId": "text",
"ticket": {
"asana": {
"projectGid": "text",
"taskGid": "text",
"url": "text"
},
"azure": {
"url": "text",
"workItemId": 1
},
"github": {
"issueId": 1,
"nodeId": "text",
"number": 1,
"repositoryName": "text",
"url": "text"
},
"gitlab": {},
"jira": {
"issueId": "text",
"issueKey": "text",
"url": "text"
},
"linear": {
"issueId": "text",
"teamId": "text",
"url": "text"
},
"providerId": "DX"
},
"ticketState": "text",
"title": "text",
"updatedAt": "text",
"userNotes": "text",
"workflow": "text"
},
"version": "text"
}Get triage details
Returns the triaged view of the finding, with the triage verdict and, when one exists, a presignedUrl for the stored triage report. A finding that has not been triaged still returns 200, with no verdict.
Returns the triage data for a specific finding
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
GET /scpm/findings/{findingId}/triage HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"finding": {
"falsePositiveReason": "text",
"finding": {
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"autoFixCurrentActivity": "text",
"autoFixEndTime": "2026-01-01T00:00:00.000Z",
"autoFixStartTime": "2026-01-01T00:00:00.000Z",
"autoFixState": "none",
"autofixVerdictAt": "2026-01-01T00:00:00.000Z",
"autofixVerdictExplanation": "text",
"autofixVerdictReason": "AGENT_ERROR",
"branch": "text",
"budgetExhausted": true,
"budgetLimit": 1,
"budgetUsed": 1,
"commitAuthor": "text",
"commitHash": "text",
"commitMessage": "text",
"commitUrl": "text",
"concurrencyLimit": 1,
"cooldownExpiresAt": "2026-01-01T00:00:00.000Z",
"cooldownPublishedAt": "2026-01-01T00:00:00.000Z",
"createdAt": "text",
"deletedAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"endLine": 1,
"enforcement": {
"mode": "off",
"severity": "CRITICAL",
"source": "default",
"suppressed": true,
"suppressedBy": ""
},
"failedTriage": true,
"falsePositiveReason": "text",
"fileOwners": [
{
"name": "text",
"type": "email"
}
],
"filePath": "text",
"findingType": "known_malware",
"firstSeenCommit": "text",
"id": "text",
"installationId": "text",
"isAllowlisted": true,
"isArchived": true,
"isAutoFixableAgentVerdict": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"isResolved": true,
"language": "text",
"lockfilePath": "text",
"manifestPath": "text",
"message": "text",
"owner": "text",
"packageManager": "text",
"packageName": "text",
"packageVersion": "text",
"platform": "text",
"priorityLabel": "URGENT",
"priorityLabelReason": "text",
"priorityOverride": "URGENT",
"priorityScore": 1,
"projectId": "text",
"projectName": "text",
"pullRequestId": "text",
"pullRequestUrl": "text",
"pullRequestsAutofix": [
{
"ciCheckedAt": "2026-01-01T00:00:00.000Z",
"ciDetailUrl": "text",
"ciState": "pending",
"createdAt": "2026-01-01T00:00:00.000Z",
"explanation": "text",
"firstReviewedAt": "2026-01-01T00:00:00.000Z",
"hasCustomerCommit": true,
"hasCustomerFeedback": true,
"id": 1,
"lastReviewedAt": "2026-01-01T00:00:00.000Z",
"link": "text",
"mergedAt": "2026-01-01T00:00:00.000Z",
"pullRequestProvider": {
"azure": {
"pullRequestId": 1
},
"bitbucket": {
"pullRequestId": 1
},
"github": {
"pullRequestNodeId": "text",
"pullRequestNumber": 1
},
"gitlab": {
"mergeRequestNumber": 1
},
"id": "DX"
},
"recordId": "text",
"status": "open",
"title": "text",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"pureInsertion": true,
"repository": "text",
"repositoryId": "text",
"resolvedAt": "2026-01-01T00:00:00.000Z",
"ruleId": "text",
"ruleSourceId": "text",
"ruleUrl": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"startLine": 1,
"tenantId": "text",
"ticket": {
"asana": {
"projectGid": "text",
"taskGid": "text",
"url": "text"
},
"azure": {
"url": "text",
"workItemId": 1
},
"github": {
"issueId": 1,
"nodeId": "text",
"number": 1,
"repositoryName": "text",
"url": "text"
},
"gitlab": {},
"jira": {
"issueId": "text",
"issueKey": "text",
"url": "text"
},
"linear": {
"issueId": "text",
"teamId": "text",
"url": "text"
},
"providerId": "DX"
},
"ticketState": "text",
"title": "text",
"updatedAt": "text",
"userNotes": "text",
"workflow": "text"
},
"fixStoryPointEstimate": 1,
"isFalsePositive": true,
"priority": {
"description": "text",
"exploitability": {
"attackComplexity": {
"description": "text",
"label": "HIGH",
"privilegesRequired": {
"description": "text",
"label": "AUTHENTICATED"
}
},
"attackVector": {
"description": "text",
"label": "ADJACENT",
"subVector": [
"LOCAL_COMMAND_LINE_ARGUMENT"
]
},
"description": "text",
"evidence": {
"edges": [
{
"confidence": 1,
"explanation": "text",
"properties": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"sourceId": "text",
"targetId": "text",
"type": "ABOUT"
}
],
"nodes": []
},
"label": "LIKELY"
},
"fixEffort": {
"description": "text",
"label": "EASY"
},
"label": "FALSE_POSITIVE",
"score": 1,
"severity": {
"availability": {
"cause": [
"BUFFER_OVERFLOW"
],
"description": "text",
"label": "HIGH"
},
"businessCriticality": {
"description": "text",
"label": "HIGH"
},
"confidentiality": {
"dataLeaked": [
"ADDRESS"
],
"label": "HIGH"
},
"description": "text",
"integrity": {
"description": "text",
"label": "HIGH",
"type": [
"BEHAVIOUR_MANIPULATED"
]
},
"label": "CRITICAL"
}
},
"repoContextVersion": "text",
"triage": {
"budgetExhausted": true,
"campaignMetadata": {
"id": "text",
"name": "text"
},
"cloudContext": {
"confidence": 1,
"mode": "MODEA",
"ownershipBasis": "declared",
"repos": [
"text"
],
"thoughts": "text"
},
"complianceImpact": {
"citations": [
{
"documentId": "text",
"endLine": 1,
"fileName": "text",
"nodeId": "text",
"page": 1,
"quote": "text",
"score": 1,
"startLine": 1,
"title": "text"
}
],
"confidence": "HIGH",
"frameworks": [
{
"clause": "text",
"framework": "CCPA",
"obligation": "text"
}
],
"rationale": "text"
},
"customerQuestions": {
"customMessage": "text",
"generatedMemoryIds": [
"text"
],
"id": "text",
"questions": [
{
"answer": null,
"answerMemoryId": "text",
"answeredAt": "2026-01-01T00:00:00.000Z",
"customMessage": "text",
"findingIds": [
"text"
],
"id": "text",
"options": [
{
"id": "text",
"label": "text",
"value": "text"
}
],
"questionText": "text",
"questionType": "FREE_TEXT",
"relatedMemoryIds": [
"text"
],
"scope": "CLOUD",
"scopeResourceId": "text",
"unknownIds": [
"text"
]
}
]
},
"description": "text",
"descriptionSummary": "text",
"devDescription": "text",
"devTitle": "text",
"evidence": {
"edges": [
{
"confidence": 1,
"explanation": "text",
"properties": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"sourceId": "text",
"targetId": "text",
"type": "ABOUT"
}
],
"nodes": []
},
"exploitHypothesis": {
"edges": [
{
"confidence": 1,
"explanation": "text",
"properties": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"sourceId": "text",
"targetId": "text",
"type": "ABOUT"
}
],
"nodes": []
},
"exploitability": {
"attackComplexity": {
"description": "text",
"exploitChainRequired": {
"description": "text",
"label": "HIGH",
"title": "text"
},
"label": "HIGH",
"payloadDifficulty": {
"description": "text",
"label": "EASY"
}
},
"attackRequirements": {
"description": "text",
"environmentState": {
"description": "text",
"label": "HIGH",
"title": "text"
},
"label": "HIGH",
"mitm": {
"description": "text",
"label": "HIGH",
"title": "text"
},
"raceCondition": {
"description": "text",
"label": "HIGH",
"title": "text"
},
"timeWindow": {
"description": "text",
"label": "HIGH",
"title": "text"
},
"title": "text"
},
"attackVector": {
"description": "text",
"label": "ADJACENT",
"subVectors": [
{
"description": "text",
"otherVector": "text",
"vector": "ADJACENT_ARP_SPOOFING"
}
]
},
"confidence": "HIGH",
"description": "text",
"label": "LIKELY",
"privilegesRequired": {
"description": "text",
"label": "HIGH",
"title": "text",
"types": [
"PRIVILEGED_USER"
]
},
"title": "text",
"unknowns": [
"text"
],
"userInteraction": {
"description": "text",
"label": "ACTIVE",
"otherTypes": [
"text"
],
"title": "text",
"types": [
"BROWSING"
]
}
},
"failedTriage": true,
"fixEffort": {
"confidence": "HIGH",
"description": "text",
"label": "EASY",
"title": "text",
"unknowns": [
"text"
]
},
"id": "text",
"impact": {
"availability": {
"cause": [
"BUFFER_OVERFLOW"
],
"description": "text",
"label": "HIGH",
"title": "text",
"type": [
"COMPLETE_SERVICE_OUTAGE"
]
},
"businessCriticality": {
"description": "text",
"label": "HIGH",
"title": "text"
},
"confidence": "HIGH",
"confidentiality": {
"dataLeaked": [
"ACCESS_TOKEN"
],
"label": "HIGH"
},
"description": "text",
"integrity": {
"description": "text",
"label": "HIGH",
"title": "text",
"type": [
"DATABASE_ALTERED"
]
},
"label": "CRITICAL",
"title": "text",
"unknowns": [
"text"
]
},
"investigationConfidence": "HIGH",
"investigationId": "text",
"investigationUnknowns": [
"text"
],
"lastTriagedAt": "2026-01-01T00:00:00.000Z",
"memoriesGenerated": [
"text"
],
"memoriesUsed": [
"text"
],
"parentTriageId": "text",
"pentest": {
"attemptedCVEs": [
"text"
],
"browserEvidence": {
"citation": "text",
"family": "text",
"reason": "text",
"screenshotRefs": [
"text"
],
"verdict": "text"
},
"confidence": 1,
"cost": 1,
"duration": 1,
"exploitedCVE": "text",
"logs": [
{
"logs": [],
"summary": "text"
}
],
"mutationDisclosure": {
"writes": [
{
"method": "text",
"outcome": "text",
"statusCode": 1,
"url": "text"
}
],
"writesAttempted": true
},
"outcome": "CANNOT_DETERMINE",
"report": "text",
"reproductionScript": "text",
"sourceAvailability": {
"apiSchema": true,
"checkout": true,
"codeSnippets": true
},
"testedEndpoints": [
{
"method": "text",
"path": "text"
}
],
"validatedBy": {
"engine": "FINDING_EXPLOIT_VALIDATION",
"recordedAt": "2026-01-01T00:00:00.000Z",
"runId": "text"
}
},
"priority": "FALSE_POSITIVE",
"references": [
{
"documentId": "text",
"endLine": 1,
"fileName": "text",
"nodeId": "text",
"page": 1,
"quote": "text",
"score": 1,
"startLine": 1,
"title": "text"
}
],
"retriageComparison": {
"narrative": "text",
"newInformation": {
"cloudContextUpdated": true,
"memoryChanges": [
{
"description": "text",
"kind": "ADDED"
}
],
"pentestResultAdded": true,
"userComments": [
{
"commentId": "text",
"influence": "changed_verdict",
"keyClaim": "text"
}
]
},
"qualityAssessment": {
"remainingUncertainties": [
"text"
],
"sufficientEvidence": true
},
"verdictChanges": [
{
"current": "text",
"field": "text",
"previous": "text",
"reason": "text"
}
]
},
"retriageMetadata": {
"memoryChanges": [
{
"memoryChangeType": "MEMORY_ADDED",
"memoryId": "text"
}
],
"triggeredAt": "2026-01-01T00:00:00.000Z"
},
"retriageStatus": "NOT_RETRIAGED",
"severity": {
"confidence": "HIGH",
"label": "CRITICAL",
"score": 1,
"unknowns": [
"text"
]
},
"stopReason": "cost_cap",
"thoughts": "text",
"title": "text",
"version": 1
}
},
"presignedUrl": "text",
"version": "text",
"vulnManagementFrameworks": {
"results": [
{
"controls": [
{
"confidence": "exact",
"id": "text",
"level": "text",
"relationship": "text",
"title": "text",
"url": "text"
}
],
"framework": "cvss-3.1",
"mappingConfidence": "exact",
"notes": [
"text"
],
"rank": "text",
"score": {
"baseScore": 1,
"nomenclature": "text",
"qualitative": "text",
"synthesized": true,
"vector": "text"
},
"version": "text"
}
]
}
}Get related users
Returns the people associated with the finding, as candidates to notify or assign.
Returns the users associated with a specific finding
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
GET /scpm/findings/{findingId}/users HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"users": [
{
"reason": "text",
"user": {
"asana": {
"id": "text"
},
"azure": {
"id": "text"
},
"azureEntra": {
"id": "text"
},
"bitbucket": {
"id": "text"
},
"createdAt": "text",
"createdBy": "text",
"deleted": true,
"displayName": "text",
"email": "text",
"github": {
"id": 1,
"nodeId": "text"
},
"gitlab": {
"id": 1
},
"googleWorkspace": {
"id": "text"
},
"id": "text",
"isBot": true,
"isRestricted": true,
"jira": {
"id": "text",
"name": "text"
},
"lastUpdatedBy": "text",
"linear": {
"id": "text"
},
"name": "text",
"okta": {
"id": "text"
},
"provider": {
"asana": {
"id": "text"
},
"azure": {
"id": "text"
},
"azureEntra": {
"id": "text"
},
"bitBucket": {
"id": "text"
},
"gitHub": {
"id": 1,
"nodeId": "text"
},
"gitlab": {
"id": 1
},
"googleWorkspace": {
"id": "text"
},
"id": "DX",
"jira": {
"id": "text",
"name": "text"
},
"linear": {
"id": "text"
},
"okta": {
"id": "text"
},
"slack": {
"id": "text",
"name": "text"
},
"teams": {
"id": "text",
"teamIDs": [
"text"
]
}
},
"realName": "text",
"slack": {
"id": "text",
"name": "text"
},
"slug": "text",
"teams": {
"id": "text",
"teamIDs": [
"text"
]
},
"updatedAt": "text",
"username": "text"
}
}
],
"version": "text"
}Get a finding's events
Returns every event recorded against the finding, newest first. The list is not paginated.
Returns the events recorded against a given finding, newest first.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
GET /scpm/findings/{findingId}/events HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"events": [
{
"data": null,
"id": "text",
"time": "text",
"timestampUnix": 1,
"type": "org_context_agent_started"
}
],
"nextToken": "text",
"numItems": 1,
"version": "text"
}Get events
Returns the tenant's SCPM events, newest first. Filter with fromTime (a UTC timestamp such as 2026-01-31T00:00:00Z), repeatable eventType, and findingId for one finding, which also returns 404 or 403 for an ID that is not yours. Sort with sort (asc or desc). limit is 1 to 100 (default 100); follow nextToken. Returns 503 when the event store is temporarily unavailable, which is safe to retry.
Returns the tenant's SCPM events, newest first by default. Filter by fromTime (RFC 3339), eventType (repeatable) and findingId; page with nextToken and limit (at most 100 per request); sort asc or desc.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
Service Unavailable
GET /scpm/events HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"events": [
{
"data": null,
"id": "text",
"time": "text",
"timestampUnix": 1,
"type": "org_context_agent_started"
}
],
"nextToken": "text",
"numItems": 1,
"version": "text"
}Allowlisting
Allowlist a finding
Suppresses a finding you have accepted. allowlistReason and allowlistType (for example UserFixed, UserAssumeRisk, UserFalsePositive or UserOther) are required by the schema, and a missing reason returns 400. Allowlisting resolves the finding, records an event, and closes its ticket on a best-effort basis. It returns no finding body.
Allowlists a finding
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
The reason for allowlisting the finding
OK
A link to the pull request
The title of the pull request
Bad Request
Forbidden
Not Found
Internal Server Error
POST /scpm/findings/{findingId}/allowlist HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 47
{
"allowlistReason": "text",
"allowlistType": "AI"
}{
"link": "text",
"title": "text",
"version": "text"
}Allowlist findings in a batch
Allowlists up to 500 findings with one allowlistReason and allowlistType, listed in findingIds. The batch is all or nothing: if any ID is not found or belongs to another tenant, the call fails and no finding in the batch is changed. Events and ticket closing then run for each finding.
Allowlist a list of SCPM findings, by findingsID
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
The reason for allowing the finding
The ID of the finding to allowlist
No Content
No content
Bad Request
Forbidden
Not Found
Internal Server Error
POST /scpm/findings/allowlist HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 69
{
"allowlistReason": "text",
"allowlistType": "AI",
"findingIds": [
"text"
]
}No content
Remove a finding from the allowlist
Reverses an allowlist decision. unallowlistReason is required. If nothing else keeps the finding resolved, it returns to the active backlog and a ticket that was closed is reopened, or a new one is created if it is past its reopen window, on a best-effort basis.
Unallowlists a finding
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
The reason for unallowlisting the finding
No Content
No content
Bad Request
Forbidden
Not Found
Internal Server Error
POST /scpm/findings/{findingId}/unallowlist HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 28
{
"unallowlistReason": "text"
}No content
Tickets
Ticket operations use the ticketing integration your tenant has configured.
Create a ticket
Creates a ticket for the finding and returns its details. The optional body takes message, assignees, project (project name or key), campaignId and campaignTitle. Failure modes: 400 if the finding already has a ticket or the Jira configuration is incomplete, 412 if ticketing is disabled, the integration needs reconnecting or an assignee is required, 501 if no ticket integration is configured, and 503 for a temporary failure resolving the assignee.
Creates a ticket for a given SCPM finding on the tenant's configured ticket integration
The ID of the SCPM finding to create a ticket for
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Precondition Failed
Internal Server Error
Not Implemented
Service Unavailable
Gateway Timeout
POST /scpm/findings/{findingId}/ticket HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 1056
{
"assignees": [
{
"asana": {
"id": "text"
},
"azure": {
"id": "text"
},
"azureEntra": {
"id": "text"
},
"bitbucket": {
"id": "text"
},
"createdAt": "text",
"createdBy": "text",
"deleted": true,
"displayName": "text",
"email": "text",
"github": {
"id": 1,
"nodeId": "text"
},
"gitlab": {
"id": 1
},
"googleWorkspace": {
"id": "text"
},
"id": "text",
"isBot": true,
"isRestricted": true,
"jira": {
"id": "text",
"name": "text"
},
"lastUpdatedBy": "text",
"linear": {
"id": "text"
},
"name": "text",
"okta": {
"id": "text"
},
"provider": {
"asana": {
"id": "text"
},
"azure": {
"id": "text"
},
"azureEntra": {
"id": "text"
},
"bitBucket": {
"id": "text"
},
"gitHub": {
"id": 1,
"nodeId": "text"
},
"gitlab": {
"id": 1
},
"googleWorkspace": {
"id": "text"
},
"id": "DX",
"jira": {
"id": "text",
"name": "text"
},
"linear": {
"id": "text"
},
"okta": {
"id": "text"
},
"slack": {
"id": "text",
"name": "text"
},
"teams": {
"id": "text",
"teamIDs": [
"text"
]
}
},
"realName": "text",
"slack": {
"id": "text",
"name": "text"
},
"slug": "text",
"teams": {
"id": "text",
"teamIDs": [
"text"
]
},
"updatedAt": "text",
"username": "text"
}
],
"campaignId": "text",
"campaignTitle": "text",
"message": "text",
"project": "text",
"userCanonicalId": "text"
}{
"ticket": {
"asana": {
"projectGid": "text",
"taskGid": "text",
"url": "text"
},
"azure": {
"url": "text",
"workItemId": 1
},
"github": {
"issueId": 1,
"nodeId": "text",
"number": 1,
"repositoryName": "text",
"url": "text"
},
"gitlab": {},
"jira": {
"issueId": "text",
"issueKey": "text",
"url": "text"
},
"linear": {
"issueId": "text",
"teamId": "text",
"url": "text"
},
"providerId": "DX"
},
"version": "text"
}Delete a ticket
Closes the finding's ticket on the integration and clears Nullify's tracking. Idempotent: a finding with no ticket still returns success.
Removes the ticket associated with an SCPM finding: closes it on the configured integration and clears Nullify's tracking. Idempotent.
The ID of the SCPM finding whose ticket should be removed
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Precondition Failed
Internal Server Error
Not Implemented
DELETE /scpm/findings/{findingId}/ticket HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"version": "text"
}Autofix
Autofix generates a fix and opens a pull request. It runs asynchronously, so a successful start response means the request was accepted, not that a pull request exists.
Start autofix
Starts a fresh fix against the current target branch. The response returns the new autoFixState and a runId. The optional body takes message, assignees, originCampaignId and force; the legacy useCachedFix field is ignored. Only findings from push scans can be fixed: others return 400. A request while a run is already in flight returns 409 (as does a finding that is resolved or already has an open pull request), an archived repository returns 412, and a finding the agent previously judged not fixable returns 400 unless you send force=true with a non-empty message.
Generates a fresh fix against the current target branch and creates a pull request. Legacy useCachedFix is ignored.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
Deprecated and ignored; SCPM always generates a fresh fix
OK
Bad Request
Forbidden
Not Found
Conflict
Precondition Failed
Internal Server Error
POST /scpm/findings/{findingId}/autofix/fix HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 1030
{
"assignees": [
{
"asana": {
"id": "text"
},
"azure": {
"id": "text"
},
"azureEntra": {
"id": "text"
},
"bitbucket": {
"id": "text"
},
"createdAt": "text",
"createdBy": "text",
"deleted": true,
"displayName": "text",
"email": "text",
"github": {
"id": 1,
"nodeId": "text"
},
"gitlab": {
"id": 1
},
"googleWorkspace": {
"id": "text"
},
"id": "text",
"isBot": true,
"isRestricted": true,
"jira": {
"id": "text",
"name": "text"
},
"lastUpdatedBy": "text",
"linear": {
"id": "text"
},
"name": "text",
"okta": {
"id": "text"
},
"provider": {
"asana": {
"id": "text"
},
"azure": {
"id": "text"
},
"azureEntra": {
"id": "text"
},
"bitBucket": {
"id": "text"
},
"gitHub": {
"id": 1,
"nodeId": "text"
},
"gitlab": {
"id": 1
},
"googleWorkspace": {
"id": "text"
},
"id": "DX",
"jira": {
"id": "text",
"name": "text"
},
"linear": {
"id": "text"
},
"okta": {
"id": "text"
},
"slack": {
"id": "text",
"name": "text"
},
"teams": {
"id": "text",
"teamIDs": [
"text"
]
}
},
"realName": "text",
"slack": {
"id": "text",
"name": "text"
},
"slug": "text",
"teams": {
"id": "text",
"teamIDs": [
"text"
]
},
"updatedAt": "text",
"username": "text"
}
],
"force": true,
"message": "text",
"originCampaignId": "text",
"useCachedFix": true
}{
"autoFixState": "none",
"error": "text",
"runId": "text",
"version": "text"
}Get autofix status
A lightweight lifecycle status for frequent polling after you start a run. It does not contact the git provider:
Lightweight per-finding autofix lifecycle status for UI polling.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
GET /scpm/findings/{findingId}/autofix/status HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"status": {
"autofixId": "text",
"autofixVerdictExplanation": "text",
"autofixVerdictReason": "AGENT_ERROR",
"availableActions": [
{
"body": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"confirm": "text",
"href": "text",
"id": "text",
"kind": "command",
"label": "text",
"method": "text"
}
],
"coordination": {
"entries": [
{
"attemptId": "text",
"blocked": true,
"message": "text",
"prIdentifier": "text",
"reasonCode": "text",
"repositoryId": "text",
"requestId": "text",
"retryAllowed": true,
"state": "text"
}
],
"unavailable": true
},
"currentActivity": "text",
"currentPhase": "planning",
"isAutoFixable": true,
"iterations": {
"count": 1,
"durationSeconds": 1,
"lastIterationAt": "text",
"totalCostUSD": 1,
"totalInputTokens": 1,
"totalOutputTokens": 1
},
"lastActivityAt": "text",
"pullRequestState": "open",
"pullRequestUrl": "text",
"pullRequests": [
{
"ciCheckedAt": "2026-01-01T00:00:00.000Z",
"ciDetailUrl": "text",
"ciState": "pending",
"createdAt": "text",
"identifier": 1,
"mergedAt": "text",
"provider": "DX",
"recordId": "text",
"status": "open",
"url": "text"
}
],
"reconciliationStale": true,
"state": "none",
"terminal": true,
"updatedAt": "text"
},
"version": "text"
}Get autofix state
The authoritative state. It also reconciles recorded pull requests with the git provider, so it is slower; poll it about every 30 seconds alongside the status call:
Authoritative per-finding autofix state — runs stale-state rescue + live git-platform reconcile. Slow by design; the dashboard polls this on a ~30s cadence alongside the fast /autofix/status.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
GET /scpm/findings/{findingId}/autofix/state HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"status": {
"autofixId": "text",
"autofixVerdictExplanation": "text",
"autofixVerdictReason": "AGENT_ERROR",
"availableActions": [
{
"body": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"confirm": "text",
"href": "text",
"id": "text",
"kind": "command",
"label": "text",
"method": "text"
}
],
"coordination": {
"entries": [
{
"attemptId": "text",
"blocked": true,
"message": "text",
"prIdentifier": "text",
"reasonCode": "text",
"repositoryId": "text",
"requestId": "text",
"retryAllowed": true,
"state": "text"
}
],
"unavailable": true
},
"currentActivity": "text",
"currentPhase": "planning",
"isAutoFixable": true,
"iterations": {
"count": 1,
"durationSeconds": 1,
"lastIterationAt": "text",
"totalCostUSD": 1,
"totalInputTokens": 1,
"totalOutputTokens": 1
},
"lastActivityAt": "text",
"pullRequestState": "open",
"pullRequestUrl": "text",
"pullRequests": [
{
"ciCheckedAt": "2026-01-01T00:00:00.000Z",
"ciDetailUrl": "text",
"ciState": "pending",
"createdAt": "text",
"identifier": 1,
"mergedAt": "text",
"provider": "DX",
"recordId": "text",
"status": "open",
"url": "text"
}
],
"reconciliationStale": true,
"state": "none",
"terminal": true,
"updatedAt": "text"
},
"version": "text"
}Get autofix activity
Cursor-paginated log of what the run did. Page with since_id set to the nextCursor of the previous response; limit defaults to 50 and is clamped to 1 to 200. The list is empty when no run exists.
Cursor-paginated autofix activity log for UI polling. Returns empty list when no autofix run exists. limit defaults to 50 and is clamped to [1, 200]; since_id is the ID of the last entry the client has already seen (use the nextCursor value from a prior response).
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
GET /scpm/findings/{findingId}/autofix/activity HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"entries": [
{
"content": "text",
"createdAt": "text",
"eventType": "text",
"id": "text",
"metadata": "text"
}
],
"nextCursor": "text"
}Get the autofix diff (retired)
Fix previews are retired for SCPM. After confirming the finding exists, this call always returns 404, so do not rely on it. Start a fresh fix with Start autofix and read the resulting pull request.
Retired: returns 404 without reading preview storage.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Internal Server Error
GET /scpm/findings/{findingId}/autofix/cache/diff HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"commit": "text",
"diffs": [
{
"header": "text",
"hunks": [
{
"header": "text",
"wholeRange": {
"length": 1,
"lines": [
{
"content": "text",
"mode": 1,
"number": 1
}
],
"start": 1
}
}
],
"mode": 1,
"newName": "text",
"origName": "text"
}
],
"explanation": "text",
"pullRequestBody": "text",
"pullRequestTitle": "text",
"version": "text"
}Findings upload
Get an upload URL
This operation is not available: it always returns 500, so it cannot be used to upload findings.
Returns a presigned URL
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
POST /scpm/findings/upload HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 17
{
"scanId": "text"
}{
"s3UploadURL": "text",
"version": "text"
}Repositories and scan runs
List repositories
Returns the repositories that carry SCPM findings as a map keyed by repository ID, each with its severity histogram and the timestamp of its most recent finding. limit defaults to 25 and is capped at 100; follow nextToken.
Returns the repositories carrying SCPM findings, keyed by repositoryID, each with its severity histogram and the timestamp of its most recent finding.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
GET /scpm/repositories HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"nextToken": "text",
"numItems": 1,
"repositories": {
"ANY_ADDITIONAL_PROPERTY": {
"codePaths": {
"exclude": [
"text"
],
"include": [
"text"
]
},
"countsBySeverity": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"countsByType": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"defaultBranch": "text",
"defaultBranchCommittedAt": "2026-01-01T00:00:00.000Z",
"defaultBranchSha": "text",
"id": "text",
"isArchived": true,
"isDefaultBranch": true,
"isEnrolled": true,
"language": "text",
"languages": [
"text"
],
"owner": "text",
"ownerType": "text",
"pii": {
"collections": [
{
"collectionId": "text",
"collections": [
{
"occurrences": [
{
"columnNumber": 1,
"excerpt": "text",
"fileName": "text",
"lineNumber": 1,
"sample": "text"
}
],
"sourceId": "text"
}
],
"isSensitive": true,
"name": "text"
}
],
"language": "text",
"probableSinks": [
"text"
],
"processing": [
{
"occurrences": [
{
"columnNumber": 1,
"excerpt": "text",
"fileName": "text",
"lineNumber": 1,
"sample": "text"
}
],
"sourceId": "text"
}
],
"sinkProcessing": [
{
"occurrences": [
{
"columnNumber": 1,
"excerpt": "text",
"fileName": "text",
"lineNumber": 1,
"sample": "text"
}
],
"sinkId": "text"
}
],
"sources": [
{
"category": "text",
"id": "text",
"isSensitive": true,
"name": "text",
"sensitivity": "LOW",
"sourceType": "text",
"tags": {
"ANY_ADDITIONAL_PROPERTY": "text"
}
}
]
},
"platform": "text",
"repoSizeBytes": 1,
"repository": "text",
"repositoryId": "text",
"sbom": "text",
"secretsEnrolled": true,
"secretsExcludes": [
"text"
],
"tenantId": "text",
"timestamp": "text",
"timestampUnix": 1
}
},
"version": "text"
}List scan runs
Lists SCPM scan runs, newest first by default. repositoryId is optional: omit it to list runs across the tenant. Page with limit (default 10, maximum 50) and offset, sort with sort (newest or oldest), and bound the window with RFC 3339 from and to (a to before from returns 400). Each run reports finding counts by outcome and severity.
Lists SCPM scan runs for a repository, newest first
Repository ID to list scan runs for; omit to list the whole tenant
Max scan runs per page (default 10, max 50)
Pagination offset (default 0)
Sort order by start time (default newest)
Only include scan runs started at or after this RFC3339 timestamp
Only include scan runs started at or before this RFC3339 timestamp
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
Service Unavailable
GET /scpm/scan-runs HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"scanRuns": [
{
"allowlistedFindingsCount": 1,
"branch": "text",
"commitHash": "text",
"completedAt": "text",
"errorMessage": "text",
"fixedFindingsCount": 1,
"isDefaultBranch": true,
"liveFindingsCount": 1,
"newFindingsCount": 1,
"reintroducedFindingsCount": 1,
"scaOutcome": {
"coverageGaps": [
{
"ecosystems": [
"text"
],
"packageAuth": [
{
"credential": "rejected",
"dir": "text",
"ecosystem": "text",
"evidence": "text",
"registry": "text",
"tool": "text",
"version": 1
}
],
"reason": "text",
"scope": "text",
"side": "source",
"source": "sbom",
"status": "text",
"suspect": {
"firstSeen": "2026-01-01T00:00:00.000Z",
"loss": "text",
"sha256": "text",
"version": 1
},
"touchedByChange": true
}
],
"degraded": true,
"failureStage": "clone",
"keptLiveCoverage": 1,
"keptLiveNotSeenInSbom": 1,
"keptLivePackageGate": 1,
"keptLiveUnchangedManifests": 1,
"sbomAuthoritative": true,
"sbomFailureClass": "oom",
"sbomIncomplete": true,
"sbomSource": "text",
"ticketsReopenSkipped": 1
},
"scanCompletedAt": "text",
"scanRunId": "text",
"severityCritical": 1,
"severityHigh": 1,
"severityLow": 1,
"severityMedium": 1,
"startedAt": "text",
"status": "text",
"triggerType": "text"
}
],
"totalCount": 1
}Package policy
GET /scpm/settings reads the policy and PUT /scpm/settings updates it. A tenant that has never saved settings gets the platform defaults (cooldown enabled, 7-day window).
Get settings
Returns the cooldown settings (cooldownEnabled, cooldownDays, per-ecosystem cooldownOverrides and cooldownWarnOnly), the accepted-license list and the package allowlist.
Returns the per-tenant SCPM policy: dependency-cooldown settings, the accepted-license list and the package allowlist. Tenants who have never saved settings get the platform defaults (cooldown enabled, 7-day window).
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Forbidden
Internal Server Error
GET /scpm/settings HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"acceptedLicenses": [
"text"
],
"cooldownDays": 1,
"cooldownEnabled": true,
"cooldownOverrides": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"cooldownWarnOnly": true,
"packageAllowlist": [
{
"createdBy": "text",
"ecosystem": "text",
"expiresAt": "2026-01-01T00:00:00.000Z",
"namespace": "text",
"packageName": "text",
"reason": "text",
"versionRange": "text"
}
],
"version": "text"
}Update settings
Upserts the policy. cooldownEnabled and cooldownDays (0 to 365) are required on every call, so read the current settings first and send them back unchanged if you only mean to change something else. cooldownWarnOnly is the pull-request gate switch: when true, cooldown findings appear on the check run but do not fail the merge gate. Every optional field that you omit keeps its stored value. When you include cooldownOverrides (ecosystem to days, 0 to 365), acceptedLicenses (SPDX identifiers, at most 500) or packageAllowlist (at most 1000 entries), it replaces the complete stored value, so read, modify and send the whole list to add an entry, and send an empty list only to clear it. Unknown ecosystems, blank package names and an expiresAt in the past return 400. An allowlist entry identifies an ecosystem and package, optionally a namespace and versionRange; a blank versionRange exempts every version, so scope it deliberately. The response echoes the saved settings.
Upsert the per-tenant SCPM policy: dependency-cooldown settings, the accepted-license list and the package allowlist. Every field except cooldownEnabled and cooldownDays preserves its stored value when absent.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
PUT /scpm/settings HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 321
{
"acceptedLicenses": [
"text"
],
"cooldownDays": 1,
"cooldownEnabled": true,
"cooldownOverrides": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"cooldownWarnOnly": true,
"packageAllowlist": [
{
"createdBy": "text",
"ecosystem": "text",
"expiresAt": "2026-01-01T00:00:00.000Z",
"namespace": "text",
"packageName": "text",
"reason": "text",
"versionRange": "text"
}
]
}{
"acceptedLicenses": [
"text"
],
"cooldownDays": 1,
"cooldownEnabled": true,
"cooldownOverrides": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"cooldownWarnOnly": true,
"packageAllowlist": [
{
"createdBy": "text",
"ecosystem": "text",
"expiresAt": "2026-01-01T00:00:00.000Z",
"namespace": "text",
"packageName": "text",
"reason": "text",
"versionRange": "text"
}
],
"version": "text"
}Malware analysis
Malware analysis covers a specific package version or container image. It is separate from looking up known CVEs in Dependency Analysis. A missing analysis result is not a clean verdict.
Request dependency analysis
Queues a package version for malware analysis. ecosystem, name and version are required (400 otherwise); previousVersion supplies a diff baseline. Requests are idempotent for 24 hours on the same coordinate, or on your own idempotencyKey if you send one. The response gives a jobId, status, cacheHit and a verdict when one is already known. There is no job-lookup operation in this API: to follow progress, send the same request again or query the package with Batch query dependencies and read analysisStatus. Requires configuration write access.
Queues a dependency version for malware analysis. Idempotent within 24h on (ecosystem, name, version) OR the caller-supplied idempotencyKey.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
Package ecosystem (npm, pypi, go, maven, rubygems, crates.io, ...)
Stable key from the caller — retries within 24h coalesce onto the same job
Package name
Optional prior version used as the diff baseline
Package version to analyse
OK
Bad Request
Forbidden
Internal Server Error
POST /scpm/dependencies/analyze HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 100
{
"ecosystem": "text",
"idempotencyKey": "text",
"name": "text",
"previousVersion": "text",
"version": "text"
}{
"cacheHit": true,
"jobId": "text",
"status": "text",
"verdict": "text",
"version": "text"
}Request container analysis
Queues an OCI image for malware analysis. registry, repository and reference (a tag or a sha256: digest) are required; previousReference supplies a diff baseline. It is idempotent for 24 hours in the same way as dependency analysis and returns the same jobId, status, cacheHit and verdict fields. Requires configuration write access.
Queues a container image for malware analysis. Idempotent within 24h on (registry, repository, reference) OR caller-supplied idempotencyKey.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
Stable key from the caller — retries within 24h coalesce onto the same job
Optional prior tag/digest used as the diff baseline
Tag OR sha256 digest, e.g. '1.25.4' or 'sha256:...'
Image registry hostname, e.g. index.docker.io or public.ecr.aws
Repository path within the registry, e.g. library/nginx
OK
Bad Request
Forbidden
Internal Server Error
POST /scpm/containers/analyze HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 109
{
"idempotencyKey": "text",
"previousReference": "text",
"reference": "text",
"registry": "text",
"repository": "text"
}{
"cacheHit": true,
"jobId": "text",
"status": "text",
"verdict": "text",
"version": "text"
}Batch query dependencies
Looks up known vulnerabilities and the malware verdict for up to 1000 package coordinates (ecosystem, name, version) in one call; an empty list or more than 1000 returns 400. Optional registryHost, resolvedUrl, integrity and provenance from your lockfile let the index check the artifact identity, and a mismatch is reported as identityMismatch. Each result carries analysisStatus (complete, queued, tier2_running, none or unknown) and found. Only complete is a verdict: a coordinate the index has never seen comes back as unknown with found: false, which is absence of evidence, not a pass, so a gate should treat anything other than complete as unverified. Results also report whether your package allowlist covers the coordinate (allowlisted, with reason and expiry), but the verdict fields are not changed by it. integrityBudgetExhausted means some rows were never digest-compared. Requires configuration read access.
Batch vulnerability and malware-verdict lookup for authenticated callers, the route the Nullify Defender action asks. Caps at 1000 packages per request. Each row carries the resolved artifact identity the index checked (registryHost, resolvedUrl, integrity, integrityVerified, provenanceVerified, provenanceMissing) and whether the tenant's own package allowlist covers the coordinate, with the entry's reason and expiry. A coordinate the index has never seen is returned with analysisStatus unknown and found false, never as a clean row; integrityBudgetExhausted reports that some rows were never digest-compared at all.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
POST /scpm/dependencies/query/batch HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 148
{
"packages": [
{
"ecosystem": "text",
"integrity": "text",
"name": "text",
"provenance": "text",
"registryHost": "text",
"resolvedUrl": "text",
"version": "text"
}
]
}{
"integrityBudgetExhausted": true,
"results": [
{
"advisoriesUnavailable": true,
"allowlistExpiresAt": "2026-01-01T00:00:00.000Z",
"allowlistReason": "text",
"allowlisted": true,
"analysisStatus": "complete",
"ecosystem": "text",
"found": true,
"identityMismatch": true,
"identityMismatchReason": "text",
"integrityUnchecked": true,
"integrityUncheckedReason": "text",
"integrityVerified": true,
"isMalicious": true,
"malwareVerdict": "text",
"name": "text",
"provenanceMissing": true,
"provenanceVerified": true,
"version": "text",
"vulnerabilities": [
{
"advisoryId": "text",
"aliases": [
"text"
],
"cvssScore": 1,
"fixedVersion": "text",
"isKev": true,
"isMalware": true,
"severity": "CRITICAL",
"summary": "text"
}
]
}
],
"version": "text"
}Published operation index
This index comes from the published specification. Check its request and response schemas before calling an operation; deployment versions and feature availability can differ.
POST
/scpm/containers/analyze
Request malware analysis for an OCI container image
POST
/scpm/dependencies/analyze
Request malware analysis for a dependency
POST
/scpm/dependencies/query/batch
Batch query vulnerabilities + malware verdict for dependencies
GET
/scpm/events
Get SCPM Events
GET
/scpm/findings
Get SCPM Findings
POST
/scpm/findings/allowlist
Allowlist Batch of SCPM Findings
GET
/scpm/findings/detailed
Get Findings Detailed
GET
/scpm/findings/preview
Get SCPM Findings
POST
/scpm/findings/upload
Get Presigned URL to Upload SCPM Findings
GET
/scpm/findings/{findingId}
Get Finding
PATCH
/scpm/findings/{findingId}
Update Finding
POST
/scpm/findings/{findingId}/allowlist
Allowlist Finding
GET
/scpm/findings/{findingId}/autofix/activity
Get SCPM Finding Autofix Activity
GET
/scpm/findings/{findingId}/autofix/cache/diff
Get SCPM Finding Autofix Diff
POST
/scpm/findings/{findingId}/autofix/fix
Autofix Finding
GET
/scpm/findings/{findingId}/autofix/state
Get SCPM Finding Autofix State
GET
/scpm/findings/{findingId}/autofix/status
Get SCPM Finding Autofix Status
GET
/scpm/findings/{findingId}/events
Get Finding Events
DELETE
/scpm/findings/{findingId}/ticket
Delete Ticket for SCPM Finding
POST
/scpm/findings/{findingId}/ticket
Create Ticket for SCPM Finding
GET
/scpm/findings/{findingId}/triage
Get Triaged Finding
POST
/scpm/findings/{findingId}/unallowlist
Unallowlist Finding
GET
/scpm/findings/{findingId}/users
Get Finding Users
GET
/scpm/repositories
Get Repositories
GET
/scpm/scan-runs
List Scan Runs
GET
/scpm/settings
Get SCPM Settings
PUT
/scpm/settings
Put SCPM Settings
Last updated