Bug Hunts API
Bug Hunts assess an authorized external attack surface. Read the scope and authorization guide before starting a scan. All endpoints live under /dast/bughunt on https://api.<TENANT>.nullify.ai.
A tenant has one Bug Hunt configuration (scope, suites, intensity) and any number of scans. Scans run asynchronously: starting one returns a scanId, and you poll the status operation until terminal is true. Only one Bug Hunt scan can be active for a tenant at a time; starting another while one holds the lock returns 409.
Configure scope
Get the configuration
Returns the tenant's Bug Hunt configuration. If none exists yet, a default is created and returned: enabled, empty scope, low intensity, and the dns_enumeration, port_scan, web_fingerprint and nuclei_scan suites on a daily cadence. The default has an empty scope.include, which the validate operation rejects, so add scope before saving. Requires configuration read permission.
Retrieve the BugHunt config for the tenant, creating a default if none exists
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
GET /dast/bughunt/config HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
OK
{
"config": {
"cadence": "",
"createdAt": "2026-01-01T00:00:00.000Z",
"credentials": [
{
"credentialId": "text",
"scope": "",
"selector": {
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
},
"suites": [
""
]
}
],
"enabled": true,
"id": "text",
"intensity": "low",
"ownershipProof": {
"domains": [
"text"
],
"method": "",
"token": "text",
"verifiedAt": "2026-01-01T00:00:00.000Z"
},
"roeAcknowledgedAt": "2026-01-01T00:00:00.000Z",
"rulesOfEngagement": {
"eligibleForBounty": true,
"policyText": "text",
"programUrl": "text",
"prohibitedActivities": [
"text"
],
"testingAllowed": true
},
"scope": {
"exclude": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
],
"include": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
]
},
"scopeProvenance": {
"importedAt": "2026-01-01T00:00:00.000Z",
"programName": "text",
"programSlug": "text",
"programUrl": "text",
"source": ""
},
"scopeVersion": 1,
"suites": [
{
"cadence": "",
"enabled": true,
"override": {
"authMatrix": {
"maxEndpoints": 1,
"maxRoles": 1
},
"authenticatedWalk": {
"maxDepth": 1,
"maxMinutes": 1
},
"browserUse": {
"maxPagesPerHost": 1,
"maxSeconds": 1
},
"dnsEnumeration": {
"includeSubdomains": true,
"maxSubdomainsPerDomain": 1,
"useCertificateTransparency": true
},
"fullBackendPentest": {
"maxPortsPerHost": 1,
"maxSeconds": 1
},
"lightweightBackendPentest": {
"maxRequestsPerEndpoint": 1,
"maxSeconds": 1
},
"malwareScan": {
"heuristicOnly": true,
"maxArtifacts": 1
},
"nucleiScan": {
"excludeTags": [
"text"
],
"includeTags": [
"text"
],
"rateLimit": 1,
"severityFloor": ""
},
"portScan": {
"aggressiveMode": true,
"customPortRanges": "text",
"rateLimitAllHosts": 1,
"rateLimitPerHost": 1,
"serviceDetection": true,
"threadCount": 1
},
"spaCheck": {
"checkLeakedEnv": true,
"checkSourceMaps": true
},
"webFingerprint": {
"cdnDetection": true,
"followRedirects": true,
"maxDepth": 1,
"wafDetection": true
}
},
"suite": ""
}
],
"tenantId": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"vulnClasses": [
{
"class": "",
"disabled": true,
"floor": ""
}
]
}
}Update the configuration
Replaces the tenant's configuration with the config object in the body (enabled, scope, vulnClasses, suites, credentials and intensity are required; cadence, rulesOfEngagement, scopeProvenance and ownershipProof are optional). The server validates it with the same rules as the validate operation and returns 400 with the first error if it fails. The scope version increments only when the include or exclude selectors actually change, and roEAcknowledged: true stamps the acknowledgement time. Requires configuration write permission.
Create or update BugHunt config for the tenant
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
PUT /dast/bughunt/config HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 1982
{
"config": {
"cadence": "",
"credentials": [
{
"credentialId": "text",
"scope": "",
"selector": {
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
},
"suites": [
""
]
}
],
"enabled": true,
"intensity": "low",
"ownershipProof": {
"domains": [
"text"
],
"method": "",
"token": "text",
"verifiedAt": "2026-01-01T00:00:00.000Z"
},
"roeAcknowledged": true,
"rulesOfEngagement": {
"eligibleForBounty": true,
"policyText": "text",
"programUrl": "text",
"prohibitedActivities": [
"text"
],
"testingAllowed": true
},
"scope": {
"exclude": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
],
"include": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
]
},
"scopeProvenance": {
"importedAt": "2026-01-01T00:00:00.000Z",
"programName": "text",
"programSlug": "text",
"programUrl": "text",
"source": ""
},
"suites": [
{
"cadence": "",
"enabled": true,
"override": {
"authMatrix": {
"maxEndpoints": 1,
"maxRoles": 1
},
"authenticatedWalk": {
"maxDepth": 1,
"maxMinutes": 1
},
"browserUse": {
"maxPagesPerHost": 1,
"maxSeconds": 1
},
"dnsEnumeration": {
"includeSubdomains": true,
"maxSubdomainsPerDomain": 1,
"useCertificateTransparency": true
},
"fullBackendPentest": {
"maxPortsPerHost": 1,
"maxSeconds": 1
},
"lightweightBackendPentest": {
"maxRequestsPerEndpoint": 1,
"maxSeconds": 1
},
"malwareScan": {
"heuristicOnly": true,
"maxArtifacts": 1
},
"nucleiScan": {
"excludeTags": [
"text"
],
"includeTags": [
"text"
],
"rateLimit": 1,
"severityFloor": ""
},
"portScan": {
"aggressiveMode": true,
"customPortRanges": "text",
"rateLimitAllHosts": 1,
"rateLimitPerHost": 1,
"serviceDetection": true,
"threadCount": 1
},
"spaCheck": {
"checkLeakedEnv": true,
"checkSourceMaps": true
},
"webFingerprint": {
"cdnDetection": true,
"followRedirects": true,
"maxDepth": 1,
"wafDetection": true
}
},
"suite": ""
}
],
"vulnClasses": [
{
"class": "",
"disabled": true,
"floor": ""
}
]
}
}OK
{
"config": {
"cadence": "",
"createdAt": "2026-01-01T00:00:00.000Z",
"credentials": [
{
"credentialId": "text",
"scope": "",
"selector": {
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
},
"suites": [
""
]
}
],
"enabled": true,
"id": "text",
"intensity": "low",
"ownershipProof": {
"domains": [
"text"
],
"method": "",
"token": "text",
"verifiedAt": "2026-01-01T00:00:00.000Z"
},
"roeAcknowledgedAt": "2026-01-01T00:00:00.000Z",
"rulesOfEngagement": {
"eligibleForBounty": true,
"policyText": "text",
"programUrl": "text",
"prohibitedActivities": [
"text"
],
"testingAllowed": true
},
"scope": {
"exclude": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
],
"include": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
]
},
"scopeProvenance": {
"importedAt": "2026-01-01T00:00:00.000Z",
"programName": "text",
"programSlug": "text",
"programUrl": "text",
"source": ""
},
"scopeVersion": 1,
"suites": [
{
"cadence": "",
"enabled": true,
"override": {
"authMatrix": {
"maxEndpoints": 1,
"maxRoles": 1
},
"authenticatedWalk": {
"maxDepth": 1,
"maxMinutes": 1
},
"browserUse": {
"maxPagesPerHost": 1,
"maxSeconds": 1
},
"dnsEnumeration": {
"includeSubdomains": true,
"maxSubdomainsPerDomain": 1,
"useCertificateTransparency": true
},
"fullBackendPentest": {
"maxPortsPerHost": 1,
"maxSeconds": 1
},
"lightweightBackendPentest": {
"maxRequestsPerEndpoint": 1,
"maxSeconds": 1
},
"malwareScan": {
"heuristicOnly": true,
"maxArtifacts": 1
},
"nucleiScan": {
"excludeTags": [
"text"
],
"includeTags": [
"text"
],
"rateLimit": 1,
"severityFloor": ""
},
"portScan": {
"aggressiveMode": true,
"customPortRanges": "text",
"rateLimitAllHosts": 1,
"rateLimitPerHost": 1,
"serviceDetection": true,
"threadCount": 1
},
"spaCheck": {
"checkLeakedEnv": true,
"checkSourceMaps": true
},
"webFingerprint": {
"cdnDetection": true,
"followRedirects": true,
"maxDepth": 1,
"wafDetection": true
}
},
"suite": ""
}
],
"tenantId": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"vulnClasses": [
{
"class": "",
"disabled": true,
"floor": ""
}
]
}
}Validate a configuration
Checks a proposed config without saving it and returns a list of typed errors with a path and message each; an empty list means it passed. It reports an empty scope.include, unknown intensity, suite, vulnerability class or selector kinds, duplicate suite or vulnerability-class rules, include and exclude selectors that overlap, forbidden targets such as loopback, private (RFC 1918), link-local or cloud metadata addresses in scope.include, and enabled authenticated suites with no credential binding. It also checks the top-level cadence value and suite overrides, but not rules of engagement or ownership proof.
Validate a BugHunt config payload without persisting it. Returns a typed slice of ConfigValidationError so the configure UI can render inline errors.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
POST /dast/bughunt/config/validate HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 1982
{
"config": {
"cadence": "",
"credentials": [
{
"credentialId": "text",
"scope": "",
"selector": {
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
},
"suites": [
""
]
}
],
"enabled": true,
"intensity": "low",
"ownershipProof": {
"domains": [
"text"
],
"method": "",
"token": "text",
"verifiedAt": "2026-01-01T00:00:00.000Z"
},
"roeAcknowledged": true,
"rulesOfEngagement": {
"eligibleForBounty": true,
"policyText": "text",
"programUrl": "text",
"prohibitedActivities": [
"text"
],
"testingAllowed": true
},
"scope": {
"exclude": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
],
"include": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
]
},
"scopeProvenance": {
"importedAt": "2026-01-01T00:00:00.000Z",
"programName": "text",
"programSlug": "text",
"programUrl": "text",
"source": ""
},
"suites": [
{
"cadence": "",
"enabled": true,
"override": {
"authMatrix": {
"maxEndpoints": 1,
"maxRoles": 1
},
"authenticatedWalk": {
"maxDepth": 1,
"maxMinutes": 1
},
"browserUse": {
"maxPagesPerHost": 1,
"maxSeconds": 1
},
"dnsEnumeration": {
"includeSubdomains": true,
"maxSubdomainsPerDomain": 1,
"useCertificateTransparency": true
},
"fullBackendPentest": {
"maxPortsPerHost": 1,
"maxSeconds": 1
},
"lightweightBackendPentest": {
"maxRequestsPerEndpoint": 1,
"maxSeconds": 1
},
"malwareScan": {
"heuristicOnly": true,
"maxArtifacts": 1
},
"nucleiScan": {
"excludeTags": [
"text"
],
"includeTags": [
"text"
],
"rateLimit": 1,
"severityFloor": ""
},
"portScan": {
"aggressiveMode": true,
"customPortRanges": "text",
"rateLimitAllHosts": 1,
"rateLimitPerHost": 1,
"serviceDetection": true,
"threadCount": 1
},
"spaCheck": {
"checkLeakedEnv": true,
"checkSourceMaps": true
},
"webFingerprint": {
"cdnDetection": true,
"followRedirects": true,
"maxDepth": 1,
"wafDetection": true
}
},
"suite": ""
}
],
"vulnClasses": [
{
"class": "",
"disabled": true,
"floor": ""
}
]
}
}OK
{
"errors": [
{
"code": "none",
"message": "text",
"path": "text"
}
]
}Preview a bug-bounty program import
Fetches a public HackerOne or Bugcrowd program page and returns the parsed scope, rulesOfEngagement and scopeProvenance for you to review. Send programType (hackerone or bugcrowd) and programUrl. This is preview only and saves nothing: inspect the domains, exclusions and program rules, then save what you accept with the update-configuration operation. A public program's existence is not blanket permission to test every related host. An unsupported type, empty URL or a program that cannot be fetched or parsed returns 400. Requires configuration write permission.
Fetch and parse a public bug-bounty program (HackerOne/Bugcrowd) into a typed ScopePolicy, RulesOfEngagement, and ScopeProvenance for human confirmation. PREVIEW ONLY — it never commits to the tenant's config; the operator confirms then PUTs /dast/bughunt/config.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Internal Server Error
POST /dast/bughunt/program/import HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 42
{
"programType": "text",
"programUrl": "text"
}{
"rulesOfEngagement": {
"eligibleForBounty": true,
"policyText": "text",
"programUrl": "text",
"prohibitedActivities": [
"text"
],
"testingAllowed": true
},
"scope": {
"exclude": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": [
"[Circular Reference]"
],
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
],
"include": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": [
"[Circular Reference]"
],
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
]
},
"scopeProvenance": {
"importedAt": "2026-01-01T00:00:00.000Z",
"programName": "text",
"programSlug": "text",
"programUrl": "text",
"source": ""
}
}Run scans
Start a scan
Starts a scan and returns its scanId. Send intensity (low, medium or high) and a scope with include and exclude selectors. If you send an empty scope, the stored configuration's scope is used; if you send a non-empty scope, it is used for this scan and also written to the tenant's configuration (keeping the stored suites, vulnerability classes and credential bindings). The call returns 409 if the tenant already has an active Bug Hunt scan, and 500 if the scan could not be started, in which case the scan is recorded as failed. Starting a scan is asynchronous: poll the status operation. Requires finding write permission.
Start a new BugHunt scan
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
POST /dast/bughunt/scans HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 367
{
"intensity": "low",
"scope": {
"exclude": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": [
"[Circular Reference]"
],
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
],
"include": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": [
"[Circular Reference]"
],
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
]
}
}OK
{
"scanId": "text"
}Run a single suite now
Starts an ad-hoc scan that runs only the suite named in the path (for example port_scan or nuclei_scan) against the stored configuration's scope, and returns the new scanId. An unknown suite returns 400. The optional intensity body field overrides the stored intensity, which itself defaults to medium if unset. Like a normal scan it is exclusive: it returns 409 while another Bug Hunt scan is active. Poll the returned scan with the status operation. Requires finding write permission.
Manually trigger a single bug hunt test suite for the tenant. Honours the tenant's stored intensity unless the request supplies an override.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
POST /dast/bughunt/suites/{suite}/run-now HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 19
{
"intensity": "low"
}OK
{
"scanId": "text"
}List scans
Lists the tenant's scans. With no query parameters it returns every scan. With limit (default 50, maximum 1000), offset, nextToken or search (text match over scan id, status and targets) it returns a page with totalCount; pass the returned nextToken for the next page (see pagination). An invalid nextToken returns 400.
List BugHunt scans for tenant
Pagination offset; overrides nextToken when > 0
Server-side text search over scan id / status / targets
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
GET /dast/bughunt/scans HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
OK
{
"nextToken": "text",
"scans": [
{
"assessmentPhases": [
{
"durationSeconds": 1,
"endTime": "2026-01-01T00:00:00.000Z",
"groupedLogs": [
{
"logs": [],
"summary": "text"
}
],
"logs": [],
"name": "text",
"startTime": "2026-01-01T00:00:00.000Z",
"status": "text",
"usageMetrics": {
"cacheHitCost": 1,
"cacheHitTokenCount": 1,
"cacheWriteCost": 1,
"cacheWriteTokenCount": 1,
"inputCost": 1,
"inputTokenCount": 1,
"outputCost": 1,
"outputTokenCount": 1,
"totalCost": 1
}
}
],
"createdAt": "2026-01-01T00:00:00.000Z",
"endTime": "2026-01-01T00:00:00.000Z",
"endedAt": "2026-01-01T00:00:00.000Z",
"executionArn": "text",
"failureReason": "text",
"findingIds": [
"text"
],
"findings": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
],
"id": "text",
"intensity": "low",
"localImport": true,
"metrics": {
"domains": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"endpoints": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"ports": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"total": {
"domainNames": 1,
"endpoints": 1,
"findings": 1,
"hosts": 1,
"ipAddresses": 1,
"services": 1
}
},
"networkLogs": [
{
"timestamp": "2026-01-01T00:00:00.000Z",
"transactions": [
{
"authRedacted": true,
"correlationId": "text",
"durationMs": 1,
"endTime": "2026-01-01T00:00:00.000Z",
"error": "text",
"protocol": "text",
"request": {
"body": "text",
"headers": {
"ANY_ADDITIONAL_PROPERTY": "text"
},
"method": "text",
"url": "text"
},
"response": {
"body": "text",
"headers": {
"ANY_ADDITIONAL_PROPERTY": "text"
},
"statusCode": 1
},
"startTime": "2026-01-01T00:00:00.000Z"
}
]
}
],
"progress": 1,
"scope": {
"exclude": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": "[Circular Reference]",
"value": "text"
}
],
"include": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": "[Circular Reference]",
"value": "text"
}
]
},
"scopeVersion": 1,
"stage": "initializing",
"startTime": "2026-01-01T00:00:00.000Z",
"startedAt": "2026-01-01T00:00:00.000Z",
"status": "queued",
"suites": [
{
"cadence": "",
"enabled": true,
"override": {
"authMatrix": {
"maxEndpoints": 1,
"maxRoles": 1
},
"authenticatedWalk": {
"maxDepth": 1,
"maxMinutes": 1
},
"browserUse": {
"maxPagesPerHost": 1,
"maxSeconds": 1
},
"dnsEnumeration": {
"includeSubdomains": true,
"maxSubdomainsPerDomain": 1,
"useCertificateTransparency": true
},
"fullBackendPentest": {
"maxPortsPerHost": 1,
"maxSeconds": 1
},
"lightweightBackendPentest": {
"maxRequestsPerEndpoint": 1,
"maxSeconds": 1
},
"malwareScan": {
"heuristicOnly": true,
"maxArtifacts": 1
},
"nucleiScan": {
"excludeTags": [
"text"
],
"includeTags": [
"text"
],
"rateLimit": 1,
"severityFloor": ""
},
"portScan": {
"aggressiveMode": true,
"customPortRanges": "text",
"rateLimitAllHosts": 1,
"rateLimitPerHost": 1,
"serviceDetection": true,
"threadCount": 1
},
"spaCheck": {
"checkLeakedEnv": true,
"checkSourceMaps": true
},
"webFingerprint": {
"cdnDetection": true,
"followRedirects": true,
"maxDepth": 1,
"wafDetection": true
}
},
"suite": ""
}
],
"summary": {
"findingsBySeverity": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"findingsByType": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"totalFindings": 1,
"totalHosts": 1,
"totalPorts": 1
},
"tenantId": "text",
"totalCost": {
"costByPhase": {
"ANY_ADDITIONAL_PROPERTY": {
"cacheHitCost": 1,
"cacheHitTokenCount": 1,
"cacheWriteCost": 1,
"cacheWriteTokenCount": 1,
"inputCost": 1,
"inputTokenCount": 1,
"outputCost": 1,
"outputTokenCount": 1,
"totalCost": 1
}
},
"totalCacheHitCost": 1,
"totalCacheHitTokens": 1,
"totalCacheWriteCost": 1,
"totalCacheWriteTokens": 1,
"totalCost": 1,
"totalInputCost": 1,
"totalInputTokens": 1,
"totalOutputCost": 1,
"totalOutputTokens": 1
},
"updatedAt": "2026-01-01T00:00:00.000Z"
}
],
"totalCount": 1
}Get a scan
Returns one scan by scanId, which comes from the list or start operations. It returns 404 if the id does not exist for your tenant.
Get BugHunt scan by ID
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
GET /dast/bughunt/scans/{scanId} HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
OK
{
"scan": {
"assessmentPhases": [
{
"durationSeconds": 1,
"endTime": "2026-01-01T00:00:00.000Z",
"groupedLogs": [
{
"logs": [],
"summary": "text"
}
],
"logs": [],
"name": "text",
"startTime": "2026-01-01T00:00:00.000Z",
"status": "text",
"usageMetrics": {
"cacheHitCost": 1,
"cacheHitTokenCount": 1,
"cacheWriteCost": 1,
"cacheWriteTokenCount": 1,
"inputCost": 1,
"inputTokenCount": 1,
"outputCost": 1,
"outputTokenCount": 1,
"totalCost": 1
}
}
],
"createdAt": "2026-01-01T00:00:00.000Z",
"endTime": "2026-01-01T00:00:00.000Z",
"endedAt": "2026-01-01T00:00:00.000Z",
"executionArn": "text",
"failureReason": "text",
"findingIds": [
"text"
],
"findings": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
],
"id": "text",
"intensity": "low",
"localImport": true,
"metrics": {
"domains": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"endpoints": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"ports": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"total": {
"domainNames": 1,
"endpoints": 1,
"findings": 1,
"hosts": 1,
"ipAddresses": 1,
"services": 1
}
},
"networkLogs": [
{
"timestamp": "2026-01-01T00:00:00.000Z",
"transactions": [
{
"authRedacted": true,
"correlationId": "text",
"durationMs": 1,
"endTime": "2026-01-01T00:00:00.000Z",
"error": "text",
"protocol": "text",
"request": {
"body": "text",
"headers": {
"ANY_ADDITIONAL_PROPERTY": "text"
},
"method": "text",
"url": "text"
},
"response": {
"body": "text",
"headers": {
"ANY_ADDITIONAL_PROPERTY": "text"
},
"statusCode": 1
},
"startTime": "2026-01-01T00:00:00.000Z"
}
]
}
],
"progress": 1,
"scope": {
"exclude": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
],
"include": [
{
"assetId": {
"id": "text",
"provider": "text"
},
"kind": "",
"tagQuery": {
"children": "[Circular Reference]",
"key": "text",
"op": "AND",
"value": "text"
},
"value": "text"
}
]
},
"scopeVersion": 1,
"stage": "initializing",
"startTime": "2026-01-01T00:00:00.000Z",
"startedAt": "2026-01-01T00:00:00.000Z",
"status": "queued",
"suites": [
{
"cadence": "",
"enabled": true,
"override": {
"authMatrix": {
"maxEndpoints": 1,
"maxRoles": 1
},
"authenticatedWalk": {
"maxDepth": 1,
"maxMinutes": 1
},
"browserUse": {
"maxPagesPerHost": 1,
"maxSeconds": 1
},
"dnsEnumeration": {
"includeSubdomains": true,
"maxSubdomainsPerDomain": 1,
"useCertificateTransparency": true
},
"fullBackendPentest": {
"maxPortsPerHost": 1,
"maxSeconds": 1
},
"lightweightBackendPentest": {
"maxRequestsPerEndpoint": 1,
"maxSeconds": 1
},
"malwareScan": {
"heuristicOnly": true,
"maxArtifacts": 1
},
"nucleiScan": {
"excludeTags": [
"text"
],
"includeTags": [
"text"
],
"rateLimit": 1,
"severityFloor": ""
},
"portScan": {
"aggressiveMode": true,
"customPortRanges": "text",
"rateLimitAllHosts": 1,
"rateLimitPerHost": 1,
"serviceDetection": true,
"threadCount": 1
},
"spaCheck": {
"checkLeakedEnv": true,
"checkSourceMaps": true
},
"webFingerprint": {
"cdnDetection": true,
"followRedirects": true,
"maxDepth": 1,
"wafDetection": true
}
},
"suite": ""
}
],
"summary": {
"findingsBySeverity": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"findingsByType": {
"ANY_ADDITIONAL_PROPERTY": 1
},
"totalFindings": 1,
"totalHosts": 1,
"totalPorts": 1
},
"tenantId": "text",
"totalCost": {
"costByPhase": {
"ANY_ADDITIONAL_PROPERTY": {
"cacheHitCost": 1,
"cacheHitTokenCount": 1,
"cacheWriteCost": 1,
"cacheWriteTokenCount": 1,
"inputCost": 1,
"inputTokenCount": 1,
"outputCost": 1,
"outputTokenCount": 1,
"totalCost": 1
}
},
"totalCacheHitCost": 1,
"totalCacheHitTokens": 1,
"totalCacheWriteCost": 1,
"totalCacheWriteTokens": 1,
"totalCost": 1,
"totalInputCost": 1,
"totalInputTokens": 1,
"totalOutputCost": 1,
"totalOutputTokens": 1
},
"updatedAt": "2026-01-01T00:00:00.000Z"
}
}Poll scan status
Returns a lightweight progress view: status, stage, progress, currentActivity, findingsCount, lastActivityAt, token cost and a short per-phase list. It reads cheap database columns rather than the full scan record, so it is the call to poll on a short interval. terminal becomes true when the scan reaches a final state (completed, cancelled, failed, aborted_config_error or skipped). Returns 404 for an unknown scan.
Live-progress status poll for a bug-hunt scan: status, stage, progress, current activity, findings count, cost, and lite per-phase view. Reads the cheap Postgres columns (NOT S3) so it is safe to poll on a short interval. terminal flips true when the scan stops progressing — the UI stops polling then.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Forbidden
Not Found
Internal Server Error
GET /dast/bughunt/scans/{scanId}/status HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"cost": {
"totalCost": 1,
"totalInputTokens": 1,
"totalOutputTokens": 1
},
"currentActivity": "text",
"findingsCount": 1,
"lastActivityAt": "text",
"phases": [
{
"durationSeconds": 1,
"name": "text",
"status": "text"
}
],
"progress": 1,
"stage": "initializing",
"status": "queued",
"terminal": true
}Get scan activity
Returns the scan's activity log for live progress displays. limit defaults to 50 and is clamped to 200. When a page is full the response carries nextCursor; pass it back as since_id to fetch only newer entries. An empty list means nothing has been logged yet.
Cursor-paginated activity log for a bug-hunt scan, for UI live-progress polling. Returns an empty list when no entries exist yet. limit defaults to 50 and is clamped to [1, 200]; since_id is the ULID of the last entry the client has already seen (use the nextCursor from a prior response).
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Forbidden
Not Found
Internal Server Error
GET /dast/bughunt/scans/{scanId}/activity HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"entries": [
{
"content": "text",
"createdAt": "text",
"eventType": "text",
"id": "text",
"metadata": "text"
}
],
"nextCursor": "text"
}Stop a scan
Requests cancellation of a scan. A scan that is in progress moves to stopping and then cancelled; a scan already in a final state is left unchanged and the call still succeeds, so check the status operation rather than the success flag to confirm the outcome. Returns 404 for an unknown scan. Requires finding write permission.
Stop a running BugHunt scan
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
POST /dast/bughunt/scans/{scanId}/stop HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
OK
{
"success": true
}Get a scan's network log
Returns a short-lived presigned downloadURL for the scan's sub-agent network log. Download it promptly because the URL expires. It returns 404 if the scan does not exist or no log has been written yet.
Returns a short-lived presigned URL for downloading the bughunt sub-agent network log
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Not Found
Internal Server Error
GET /dast/bughunt/scans/{scanId}/logs HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"downloadUrl": "text",
"version": "text"
}List a scan's findings
Lists the findings recorded for one scan. With no parameters it returns all of them; with limit (default 50, maximum 1000) or offset it returns a page and totalCount.
Get findings for an BugHunt scan
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
GET /dast/bughunt/scans/{scanId}/findings HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
OK
{
"findings": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
],
"totalCount": 1
}Compare two scans
Compares the scan in the path with an older scan given in the required against query parameter and returns four lists: new, reopened, unchanged and resolved. Both scans must belong to your tenant and have status completed; otherwise the call returns 400 (or 404 for an unknown id). Each scan is limited to 5,000 findings, beyond which the call returns a resource-exhausted error. Comparing a scan with itself returns empty lists.
Diff two BugHunt scans into the four buckets New / Reopened / Unchanged / Resolved. The 'against' query parameter is the older scan id; both scans must belong to the caller's tenant and must be in a successful terminal state.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Not Found
Too Many Requests
Internal Server Error
Service Unavailable
Gateway Timeout
GET /dast/bughunt/scans/{scanId}/diff?against=text HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"new": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
],
"reopened": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
],
"resolved": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
],
"unchanged": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
]
}List suite runs
Intended to return per-suite last-run state (suite, lastRunAt, lastRunId). In the current implementation the handler always returns an empty runs list, so do not rely on it to learn when a suite last ran. Use the scan list and scan detail operations instead.
Returns the per-suite cadence state (last run at, last run id) for the tenant.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
GET /dast/bughunt/suite-runs HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
OK
{
"runs": [
{
"lastRunAt": "text",
"lastRunId": "text",
"suite": ""
}
]
}Findings
List findings
Lists every Bug Hunt finding for the tenant, across scans, in one response with no pagination. For a single scan use the scan findings operation.
List all BugHunt findings
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
GET /dast/bughunt/findings HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
OK
{
"findings": [
{
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
]
}Get a finding
Returns one finding by findingId. It returns 404 when the id does not exist for your tenant.
Get a single BugHunt finding by ID
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Forbidden
Not Found
Internal Server Error
GET /dast/bughunt/findings/{findingId} HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"finding": {
"aiDevTitle": "text",
"aiTitle": "text",
"allowlistReason": "text",
"allowlistType": "text",
"createdAt": "2026-01-01T00:00:00.000Z",
"description": "text",
"devDescription": "text",
"evidence": {
"ANY_ADDITIONAL_PROPERTY": "anything"
},
"firstSeenAt": "2026-01-01T00:00:00.000Z",
"host": "text",
"id": "text",
"isAllowlisted": true,
"isArchived": true,
"isFalsePositive": true,
"isFixed": true,
"isLatest": true,
"lastSeenAt": "2026-01-01T00:00:00.000Z",
"lastSeenScanId": "text",
"notes": "text",
"path": "text",
"port": 1,
"priorityLabel": "URGENT",
"priorityScore": 1,
"protocol": "text",
"scanId": "text",
"service": "text",
"severity": "CRITICAL",
"severityOverride": "CRITICAL",
"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"
},
"title": "text",
"type": "text",
"updatedAt": "2026-01-01T00:00:00.000Z",
"userNotes": "text"
}
}Get a finding's triage
Returns how Nullify triaged the finding, along with the vulnerability-management framework mappings derived from it. It returns 404 when the finding does not exist for your tenant.
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Forbidden
Not Found
Internal Server Error
GET /dast/bughunt/findings/{findingId}/triage HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"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
},
"vulnManagementFrameworks": {
"results": [
{
"controls": [
{
"confidence": "exact",
"id": "text",
"level": "text",
"relationship": "text",
"title": "text",
"url": "text"
}
],
"framework": "cvss-3.1",
"mappingConfidence": "exact",
"notes": [
"text"
],
"rank": "text",
"score": {
"baseScore": 1,
"nomenclature": "text",
"qualitative": "text",
"synthesized": true,
"vector": "text"
},
"version": "text"
}
]
}
}Get a finding's events
Returns the finding's event history (id, time, type, data), such as state changes.
Get events for an BugHunt finding
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
GET /dast/bughunt/findings/{findingId}/events HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
OK
{
"events": [
{
"data": null,
"id": "text",
"time": "text",
"type": "text"
}
]
}Allowlist or unallowlist a finding
Sets whether the finding is allowlisted. Send allow (true to allowlist, false to restore) and a reason; both are required. Unlike the POST allowlist routes in the Pentests API, Bug Hunt uses PATCH with a single toggle. Allowlisting closes any linked ticket, and restoring a finding that is otherwise still open reopens or recreates its ticket; ticket-sync failures are logged and do not fail the call. Requires finding write permission.
Mark/unmark an BugHunt finding as allowlisted
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
PATCH /dast/bughunt/findings/{findingId}/allowlist HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 30
{
"allow": true,
"reason": "text"
}OK
{
"success": true
}Create a ticket
Creates a ticket for the finding in the tenant's configured ticket integration. The optional body takes message, assignees and project. It can return 404 for an unknown finding and failed-precondition or unimplemented errors when no usable integration is configured.
Creates a ticket for a given BugHunt finding on the tenant's configured ticket integration
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Precondition Failed
Internal Server Error
Not Implemented
Service Unavailable
Gateway Timeout
POST /dast/bughunt/findings/{findingId}/ticket HTTP/1.1
Host: api.<TENANT>.nullify.ai
Content-Type: application/json
Accept: */*
Content-Length: 988
{
"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"
}
],
"message": "text",
"project": "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 link to it. It is idempotent: a finding with no ticket still returns success. Requires finding write permission.
Removes the ticket associated with a bughunt finding: closes it on the configured integration and clears Nullify's tracking. Idempotent.
The ID of the bughunt finding whose ticket should be removed
The Azure organization ID
The Bitbucket workspace ID
The Github owner ID
The GitLab group ID
The Nullify installation ID
Filter by Azure repository IDs
Filter by GitHub repository IDs
Filter by GitHub team ID
Filter by Bitbucket repository IDs
OK
Bad Request
Forbidden
Not Found
Precondition Failed
Internal Server Error
Not Implemented
DELETE /dast/bughunt/findings/{findingId}/ticket HTTP/1.1
Host: api.<TENANT>.nullify.ai
Accept: */*
{
"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.
GET
/dast/bughunt/config
Get BugHunt Config
PUT
/dast/bughunt/config
Put BugHunt Config
POST
/dast/bughunt/config/validate
Validate BugHunt Config
GET
/dast/bughunt/findings
Get BugHunt Findings
GET
/dast/bughunt/findings/{findingId}
Get BugHunt Finding
PATCH
/dast/bughunt/findings/{findingId}/allowlist
Allowlist BugHunt Finding
GET
/dast/bughunt/findings/{findingId}/events
Get BugHunt Finding Events
DELETE
/dast/bughunt/findings/{findingId}/ticket
Delete Ticket for BugHunt Finding
POST
/dast/bughunt/findings/{findingId}/ticket
Create Ticket for BugHunt Finding
GET
/dast/bughunt/findings/{findingId}/triage
Create Interactor []
POST
/dast/bughunt/program/import
Preview BugHunt Program Import
GET
/dast/bughunt/scans
Get BugHunt Scans
POST
/dast/bughunt/scans
Start BugHunt Scan
GET
/dast/bughunt/scans/{scanId}
Get BugHunt Scan
GET
/dast/bughunt/scans/{scanId}/activity
Get BugHunt Scan Activity
GET
/dast/bughunt/scans/{scanId}/diff
Get BugHunt Scan Diff
GET
/dast/bughunt/scans/{scanId}/findings
Get BugHunt Scan Findings
GET
/dast/bughunt/scans/{scanId}/logs
Get BugHunt Sub-Agent Network Log
GET
/dast/bughunt/scans/{scanId}/status
Get BugHunt Scan Status
POST
/dast/bughunt/scans/{scanId}/stop
Stop BugHunt Scan
GET
/dast/bughunt/suite-runs
Get BugHunt Suite Runs
POST
/dast/bughunt/suites/{suite}/run-now
Run BugHunt Suite Now
Last updated