For the complete documentation index, see llms.txt. This page is also available as Markdown.

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, or installationId.

  • 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 nextToken and limit, capped at 100 (see Pagination). Scan runs use offset instead.

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.

Get SCPM Findings

get

Returns a filtered set of SCPM findings based on query parameters

Query parameters
nextTokenstring · nullableOptional
limitinteger · nullableOptional
priorityLabelstring · nullableOptional
severitystring · nullableOptional
fileOwnerNamestring[]Optional
hasPullRequestboolean · nullableOptional

only findings that do (or do not) have a linked pull request

branchstring · nullableOptional
workflowstring · nullableOptional
repositoryIdsstring[]Optional

if not provided, all repositories will be included

ruleIdstring · nullableOptional

only findings produced by this rule (rule source id such as scpm-cicd-unpinned-action, or the finding type)

findingTypestring[]Optional

only findings of these supply-chain types (known_malware, cicd_unpinned_action, ...)

languagestring[]Optional

only findings in these languages

isResolvedboolean · nullableOptional

combination of isFixed, isFalsePositive, isAllowlisted and isArchived

isFixedboolean · nullableOptional
isFalsePositiveboolean · nullableOptional
isAllowlistedboolean · nullableOptional
isArchivedboolean · nullableOptional
sortBystring · nullableOptional
sortstring · nullableOptional
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
nextTokenstringRequired
numItemsintegerRequired
versionstringOptional
get/scpm/findings
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.

Get SCPM Findings

get

Returns a filtered set of SCPM findings based on query parameters

Query parameters
nextTokenstring · nullableOptional
limitinteger · nullableOptional
priorityLabelstring · nullableOptional
severitystring · nullableOptional
fileOwnerNamestring[]Optional
hasPullRequestboolean · nullableOptional

only findings that do (or do not) have a linked pull request

branchstring · nullableOptional
workflowstring · nullableOptional
repositoryIdsstring[]Optional

if not provided, all repositories will be included

ruleIdstring · nullableOptional

only findings produced by this rule (rule source id such as scpm-cicd-unpinned-action, or the finding type)

findingTypestring[]Optional

only findings of these supply-chain types (known_malware, cicd_unpinned_action, ...)

languagestring[]Optional

only findings in these languages

isResolvedboolean · nullableOptional

combination of isFixed, isFalsePositive, isAllowlisted and isArchived

isFixedboolean · nullableOptional
isFalsePositiveboolean · nullableOptional
isAllowlistedboolean · nullableOptional
isArchivedboolean · nullableOptional
sortBystring · nullableOptional
sortstring · nullableOptional
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
nextTokenstringRequired
numItemsintegerRequired
versionstringOptional
get/scpm/findings/preview
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.

Get Findings Detailed

get

Returns current SCPM findings in default branches

Query parameters
nextTokenstring · nullableOptional
limitinteger · nullableOptional
priorityLabelstring · nullableOptional
ruleIdstring · nullableOptional

only findings produced by this rule (rule source id or finding type)

severitystring · nullableOptional
fileOwnerNamestring[]Optional
hasPullRequestboolean · nullableOptional

only findings that do (or do not) have a linked pull request

findingTypestring[]Optional

only findings of these supply-chain types (known_malware, cicd_unpinned_action, ...)

languagestring[]Optional

only findings in these languages

branchstring · nullableOptional
workflowstring · nullableOptional
repositoryIdstring · nullableOptional

if set, only findings for this repository are returned

isResolvedboolean · nullableOptional

combination of isFixed, isFalsePositive, isAllowlisted and isArchived

isFixedboolean · nullableOptional
isFalsePositiveboolean · nullableOptional
isAllowlistedboolean · nullableOptional
isArchivedboolean · nullableOptional
gateBaseboolean · nullableOptional

orchestrator net-new base set: current-branch latest, resolution-agnostic (includes allowlisted/false-positive rows)

sortBystring · nullableOptional
sortstring · nullableOptional
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
cooldownWarnOnlybooleanOptional
nextTokenstringRequired
numItemsintegerRequired
scannedBaselinebooleanOptional
versionstringOptional
get/scpm/findings/detailed
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.

Get Finding

get

Returns a given finding with explanation and impact

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
presignedUrlstringRequired
versionstringOptional
get/scpm/findings/{findingId}
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 Finding

patch

Update a given finding with the fields provided

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
isFalsePositiveboolean · nullableOptional
priorityOverridestring · enum · nullableOptionalPossible values:
severityOverridestring · enum · nullableOptionalPossible values:
userNotesstring · nullableOptional
Responses
200

OK

application/json
versionstringOptional
patch/scpm/findings/{findingId}
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.

Get Triaged Finding

get

Returns the triage data for a specific finding

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
presignedUrlstringRequired
versionstringOptional
get/scpm/findings/{findingId}/triage
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"
      }
    ]
  }
}

Returns the people associated with the finding, as candidates to notify or assign.

Get Finding Users

get

Returns the users associated with a specific finding

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
versionstringOptional
get/scpm/findings/{findingId}/users
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.

Get Finding Events

get

Returns the events recorded against a given finding, newest first.

Path parameters
findingIdstringRequired
Query parameters
nextTokenstring · nullableOptional
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
nextTokenstringOptional
numItemsintegerRequired
versionstringOptional
get/scpm/findings/{findingId}/events
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.

Get SCPM Events

get

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.

Query parameters
nextTokenstring · nullableOptional
limitinteger · nullableOptional
fromTimestring · nullableOptional
eventTypestring[]Optional
findingIdstring · nullableOptional
sortstring · nullableOptional
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
nextTokenstringRequired
numItemsintegerRequired
versionstringOptional
get/scpm/events
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.

Allowlist Finding

post

Allowlists a finding

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
allowlistReasonstringRequired

The reason for allowlisting the finding

allowlistTypestring · enumRequiredPossible values:
Responses
200

OK

application/json
linkstringOptional

A link to the pull request

titlestringOptional

The title of the pull request

versionstringOptional
post/scpm/findings/{findingId}/allowlist
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 Batch of SCPM Findings

post

Allowlist a list of SCPM findings, by findingsID

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
allowlistReasonstringRequired

The reason for allowing the finding

allowlistTypestring · enumRequiredPossible values:
findingIdsstring[] · nullableRequired

The ID of the finding to allowlist

Responses
204

No Content

No content

post/scpm/findings/allowlist
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.

Unallowlist Finding

post

Unallowlists a finding

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
unallowlistReasonstringRequired

The reason for unallowlisting the finding

Responses
204

No Content

No content

post/scpm/findings/{findingId}/unallowlist
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.

Create Ticket for SCPM Finding

post

Creates a ticket for a given SCPM finding on the tenant's configured ticket integration

Path parameters
findingIdstringRequired

The ID of the SCPM finding to create a ticket for

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
campaignIdstringOptional
campaignTitlestringOptional
messagestringOptional
projectstringOptional
userCanonicalIdstringOptional
Responses
200

OK

application/json
versionstringOptional
post/scpm/findings/{findingId}/ticket
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.

Delete Ticket for SCPM Finding

delete

Removes the ticket associated with an SCPM finding: closes it on the configured integration and clears Nullify's tracking. Idempotent.

Path parameters
findingIdstringRequired

The ID of the SCPM finding whose ticket should be removed

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
versionstringOptional
delete/scpm/findings/{findingId}/ticket
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.

Autofix Finding

post

Generates a fresh fix against the current target branch and creates a pull request. Legacy useCachedFix is ignored.

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
forceboolean · nullableOptional
messagestringOptional
originCampaignIdstringOptional
useCachedFixbooleanOptional

Deprecated and ignored; SCPM always generates a fresh fix

Responses
200

OK

application/json
autoFixStatestring · enumRequiredPossible values:
errorstringOptional
runIdstringOptional
versionstringOptional
post/scpm/findings/{findingId}/autofix/fix
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:

Get SCPM Finding Autofix Status

get

Lightweight per-finding autofix lifecycle status for UI polling.

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
versionstringOptional
get/scpm/findings/{findingId}/autofix/status
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:

Get SCPM Finding Autofix State

get

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.

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
versionstringOptional
get/scpm/findings/{findingId}/autofix/state
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.

Get SCPM Finding Autofix Activity

get

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).

Path parameters
findingIdstringRequired
Query parameters
since_idstringOptional
limitinteger · int32Optional
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
nextCursorstringOptional
get/scpm/findings/{findingId}/autofix/activity
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.

Get SCPM Finding Autofix Diff

get

Retired: returns 404 without reading preview storage.

Path parameters
findingIdstringRequired
Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
commitstringRequired
explanationstringRequired
pullRequestBodystringRequired
pullRequestTitlestringRequired
versionstringOptional
get/scpm/findings/{findingId}/autofix/cache/diff
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.

Get Presigned URL to Upload SCPM Findings

post

Returns a presigned URL

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
scanIdstringRequired
Responses
200

OK

application/json
s3UploadURLstringRequired
versionstringOptional
post/scpm/findings/upload
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.

Get Repositories

get

Returns the repositories carrying SCPM findings, keyed by repositoryID, each with its severity histogram and the timestamp of its most recent finding.

Query parameters
nextTokenstring · nullableOptional
limitinteger · nullableOptional
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
nextTokenstringRequired
numItemsintegerRequired
versionstringOptional
get/scpm/repositories
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.

List Scan Runs

get

Lists SCPM scan runs for a repository, newest first

Query parameters
repositoryIdstringOptional

Repository ID to list scan runs for; omit to list the whole tenant

limitinteger · nullableOptional

Max scan runs per page (default 10, max 50)

offsetinteger · nullableOptional

Pagination offset (default 0)

sortstring · enum · nullableOptional

Sort order by start time (default newest)

Possible values:
fromstring · nullableOptional

Only include scan runs started at or after this RFC3339 timestamp

tostring · nullableOptional

Only include scan runs started at or before this RFC3339 timestamp

azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
totalCountintegerOptional
get/scpm/scan-runs
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.

Get SCPM Settings

get

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).

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Responses
200

OK

application/json
acceptedLicensesstring[]Optional
cooldownDaysintegerRequired
cooldownEnabledbooleanRequired
cooldownWarnOnlybooleanRequired
versionstringOptional
get/scpm/settings
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.

Put SCPM Settings

put

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.

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
acceptedLicensesstring[] · nullableOptional
cooldownDaysinteger · max: 365Required
cooldownEnabledbooleanRequired
cooldownWarnOnlyboolean · nullableOptional
Responses
200

OK

application/json
acceptedLicensesstring[]Optional
cooldownDaysintegerRequired
cooldownEnabledbooleanRequired
cooldownWarnOnlybooleanRequired
versionstringOptional
put/scpm/settings
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.

Request malware analysis for a dependency

post

Queues a dependency version for malware analysis. Idempotent within 24h on (ecosystem, name, version) OR the caller-supplied idempotencyKey.

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
ecosystemstringRequired

Package ecosystem (npm, pypi, go, maven, rubygems, crates.io, ...)

idempotencyKeystringOptional

Stable key from the caller — retries within 24h coalesce onto the same job

namestringRequired

Package name

previousVersionstringOptional

Optional prior version used as the diff baseline

versionstringRequired

Package version to analyse

Responses
200

OK

application/json
cacheHitbooleanOptional
jobIdstringOptional
statusstringOptional
verdictstringOptional
versionstringOptional
post/scpm/dependencies/analyze
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.

Request malware analysis for an OCI container image

post

Queues a container image for malware analysis. Idempotent within 24h on (registry, repository, reference) OR caller-supplied idempotencyKey.

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
idempotencyKeystringOptional

Stable key from the caller — retries within 24h coalesce onto the same job

previousReferencestringOptional

Optional prior tag/digest used as the diff baseline

referencestringRequired

Tag OR sha256 digest, e.g. '1.25.4' or 'sha256:...'

registrystringRequired

Image registry hostname, e.g. index.docker.io or public.ecr.aws

repositorystringRequired

Repository path within the registry, e.g. library/nginx

Responses
200

OK

application/json
cacheHitbooleanOptional
jobIdstringOptional
statusstringOptional
verdictstringOptional
versionstringOptional
post/scpm/containers/analyze
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 query vulnerabilities + malware verdict for dependencies

post

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.

Query parameters
azureOrganizationIdstringOptional

The Azure organization ID

bitbucketWorkspaceIdstringOptional

The Bitbucket workspace ID

githubOwnerIdinteger · int64Optional

The Github owner ID

gitlabGroupIdinteger · int64Optional

The GitLab group ID

installationIdstringOptional

The Nullify installation ID

azureRepositoryIdstring[]Optional

Filter by Azure repository IDs

githubRepositoryIdinteger · int64[]Optional

Filter by GitHub repository IDs

githubTeamIdinteger · int64Optional

Filter by GitHub team ID

bitbucketRepositoryIdstring[]Optional

Filter by Bitbucket repository IDs

Body
Responses
200

OK

application/json
integrityBudgetExhaustedbooleanOptional
versionstringOptional
post/scpm/dependencies/query/batch
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.

Method
Path
Operation

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