Pentests

Start and manage penetration test pentests

POST/v1/scans

Create a pentest

Create a self-hosted runner job for a target. APVISO records a `pending_runner` scan and returns a runner job; an online runner claims the job, performs preflight, launches the pinned scan image, and streams results back with a job-scoped token. For retests, specify the parent pentest ID and optionally limit the retest to specific finding IDs.

Requires an active self-hosted license, allowed target visibility, at least one healthy runner, available runner concurrency, and a configured immutable `APVISO_SCAN_IMAGE_DIGEST` on the platform.

Request Body

NameTypeDescription
targetIdrequired
stringID of the target to pentest. Ownership verification is not required for self-hosted targets.
runnerId
stringOptional runner ID. When omitted, any eligible online runner for the organization can claim the job.
isRetest
booleanWhether this pentest is a retest of previously found vulnerabilities
parentScanId
stringID of the original pentest when running a retest. Required if isRetest is true
findingIds
string[]Specific finding IDs to retest. If omitted during a retest, all findings from the parent pentest are retested
modelPreset
string
freelowmediumhighultra
Pentest depth preset. Model provider credentials and actual model routing are runner-local BYOK configuration.
promoCode
stringDeprecated compatibility field; ignored in self-hosted BYOK mode

Example Request

bash
curl -X POST "https://apviso.com/api/v1/scans" \
  -H "X-API-Key: apvk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "targetId": "019414a8-f7c0-7d4a-b5e3-9c2d1e8f4a6b",
  "runnerId": "runner-1",
  "modelPreset": "high"
}'

Response

Returns the new scan and runner job. The normal self-hosted lifecycle is `pending_runner` -> `assigned` -> `running` -> `completed` (or `preflight_failed`, `failed`, or `cancelled`).

json
{
  "scan": {
    "id": "019414c3-d5e6-7f8a-b9c0-1d2e3f4a5b6c",
    "targetId": "019414a8-f7c0-7d4a-b5e3-9c2d1e8f4a6b",
    "status": "pending_runner",
    "runnerId": "runner-1",
    "executionMode": "self_hosted_runner",
    "targetVisibility": "public",
    "scanImageDigest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "isRetest": false,
    "parentScanId": null,
    "config": {
      "modelPreset": "high",
      "execution": "self_hosted_runner"
    },
    "createdAt": "2026-05-03T09:30:14Z"
  },
  "runnerJob": {
    "id": "job-1",
    "scanId": "019414c3-d5e6-7f8a-b9c0-1d2e3f4a5b6c",
    "runnerId": "runner-1",
    "status": "pending_runner",
    "imageRef": "ghcr.io/apviso/scan",
    "imageDigest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "updateChannel": "stable"
  }
}
POST/v1/scans/estimate

Estimate pentest readiness

Check self-hosted license, target visibility, and runner readiness without creating a scan.

Request Body

NameTypeDescription
targetIdrequired
stringID of the target to estimate
isRetest
booleanWhether this would be a retest (retests cost less)
modelPreset
string
freelowmediumhighultra
Pentest depth preset
promoCode
stringOptional promotional code to factor into the estimate

Example Request

bash
curl -X POST "https://apviso.com/api/v1/scans/estimate" \
  -H "X-API-Key: apvk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "targetId": "019414a8-f7c0-7d4a-b5e3-9c2d1e8f4a6b",
  "modelPreset": "high"
}'

Response

Returns readiness booleans and license/runner context. `priceCents` is zero in self-hosted BYOK mode.

json
{
  "source": "self_hosted_license",
  "priceCents": 0,
  "hasPaymentMethod": true,
  "eligible": true,
  "runnerOk": true,
  "visibilityOk": true,
  "license": {
    "plan": "team",
    "licenseState": "active",
    "runnerLimit": 10,
    "concurrentJobLimit": 10
  },
  "runners": [
    {
      "id": "runner-1",
      "name": "prod-runner-1",
      "status": "online"
    }
  ]
}
GET/v1/scans

List pentests

Retrieve a paginated list of all pentests for your account. Optionally filter by pentest status. Results are ordered by creation date (newest first).

Query Parameters

NameTypeDescription
page
number
Default: 1
Page number for pagination
limit
number
Default: 20
Number of results per page (max 100)
status
string
pending_runnerassignedqueuedprovisioningpreflight_failedrunningstalecompletedfailedcancelled
Filter by pentest status

Example Request

bash
curl -X GET "https://apviso.com/api/v1/scans" \
  -H "X-API-Key: apvk_your_key_here"

Response

Returns a paginated list of pentests with findings count breakdown by severity, timing information, and duration in seconds.

json
{
  "scans": [
    {
      "id": "019414c3-d5e6-7f8a-b9c0-1d2e3f4a5b6c",
      "targetId": "019414a8-f7c0-7d4a-b5e3-9c2d1e8f4a6b",
      "target": {
        "domain": "example.com"
      },
      "status": "completed",
      "executionMode": "self_hosted_runner",
      "runnerId": "runner-1",
      "isRetest": false,
      "parentScanId": null,
      "config": {
        "modelPreset": "high",
        "execution": "self_hosted_runner"
      },
      "startedAt": "2026-05-03T09:32:08Z",
      "completedAt": "2026-05-03T10:04:52Z",
      "createdAt": "2026-05-03T09:30:14Z"
    },
    {
      "id": "019414b8-e2f3-7a4b-c5d6-7e8f9a0b1c2d",
      "targetId": "019414b2-a3d1-7e8b-c4f6-1d2e3f4a5b6c",
      "target": {
        "domain": "staging.example.com"
      },
      "status": "pending_runner",
      "executionMode": "self_hosted_runner",
      "runnerId": null,
      "isRetest": false,
      "parentScanId": null,
      "config": {
        "modelPreset": "medium",
        "execution": "self_hosted_runner"
      },
      "startedAt": null,
      "completedAt": null,
      "createdAt": "2026-05-03T10:14:50Z"
    }
  ],
  "total": 2,
  "page": 1,
  "limit": 20,
  "totalPages": 1
}
GET/v1/scans/:id

Get pentest details

Retrieve detailed information about a specific pentest, including its current status, agent progress, findings summary, and timing data.

Path Parameters

NameTypeDescription
idrequired
stringPentest ID (UUIDv7)

Example Request

bash
curl -X GET "https://apviso.com/api/v1/scans/:id" \
  -H "X-API-Key: apvk_your_key_here"

Response

Returns the full pentest object with self-hosted runner metadata, target details, status, and timing fields.

json
{
  "scan": {
    "id": "019414c3-d5e6-7f8a-b9c0-1d2e3f4a5b6c",
    "targetId": "019414a8-f7c0-7d4a-b5e3-9c2d1e8f4a6b",
    "target": {
      "domain": "example.com",
      "authConfigMode": "runner_local"
    },
    "status": "completed",
    "executionMode": "self_hosted_runner",
    "runnerId": "runner-1",
    "isRetest": false,
    "parentScanId": null,
    "config": {
      "modelPreset": "high",
      "execution": "self_hosted_runner"
    },
    "scanImageDigest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "startedAt": "2026-05-03T09:32:08Z",
    "completedAt": "2026-05-03T10:04:52Z",
    "createdAt": "2026-05-03T09:30:14Z"
  }
}
POST/v1/scans/:id/cancel

Cancel a pentest

Cancel a pentest that is currently queued or active. Self-hosted runner jobs receive a cancellation request and stop as soon as possible.

Self-hosted pentests can be cancelled while pending runner assignment, assigned, preflight failed, queued, provisioning, running, stale, or legacy pending verification.

Path Parameters

NameTypeDescription
idrequired
stringPentest ID (UUIDv7)

Example Request

bash
curl -X POST "https://apviso.com/api/v1/scans/:id/cancel" \
  -H "X-API-Key: apvk_your_key_here"

Response

Returns the updated pentest status. Self-hosted BYOK mode does not issue APVISO-hosted scan refunds.

json
{
  "scan": {
    "id": "019414c3-d5e6-7f8a-b9c0-1d2e3f4a5b6c",
    "status": "cancelled",
    "completedAt": "2026-05-03T09:31:02Z"
  },
  "refunded": false
}