Vulnix
API

Scopes

Define what the agent is allowed to test — targets, constraints, context, and attack surface — before starting a run.

A scope is the authorization boundary for a pentest: what may be tested, how hard, and when. A run always executes against one scope, so a scope is what you build first.

The scope object

GET /scopes/{scope_id} returns the full picture:

Prop

Type

`scope_enabled` is not a launch flag

It toggles attack-surface monitoring. A run can start on a scope where it is false, and turning it on does not make a scope launch-ready. The launch preconditions are on the Runs page.

Create a scope

POST /scopes · scope scopes:write · role admin

Prop

Type

curl -X POST https://api.vulnix.dev/scopes \
  -H "Authorization: Bearer $VULNIX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scan_type": "blackbox"}'
{
  "id": "01M2NMSFVBHZTYR842YNP45TF3",
  "org_id": "01K5ADMINORG000000000000A",
  "status": "draft"
}

List scopes

GET /scopes · scope scopes:read · role admin

A summary per scope — enough to pick one without fetching each in full:

{
  "scopes": [
    {
      "id": "01M2NMSFVBHZTYR842YNP45TF3",
      "status": "draft",
      "target_count": 1,
      "all_targets_verified": false
    }
  ]
}

all_targets_verified is the field to check before starting a run — it is precisely the condition that otherwise fails with scope_verification_lost.

Targets

A target is one thing to test. It must be verified before a run can use it.

Prop

Type

Add a target

POST /scopes/{scope_id}/targets · scope scopes:write · role admin

Prop

Type

{
  "id": "01M2NMT9770P4XX9AB8PCZZGB1",
  "domain_id": "01M2NMSF7GJY6KMMKZJ0EA0N47",
  "target_type": "domain",
  "value": "acme-demo.com",
  "verification_status": "pending",
  "verification_failure_reason": null,
  "verification_txt_host": "_vulnix-verify.acme-demo.com",
  "verification_txt_value": "vulnix-verify=86b64bbb2b229da31a35e9f3f63253b7"
}

Adding a target for a host you have not registered creates the domain record too, which is why a domain_id comes back. An unrecognized target_type or value returns 422 invalid_scope_target.

Verify a target

POST /scopes/{scope_id}/targets/{target_id}/verify · scope scopes:write · role admin

Re-checks the published proof and returns the updated target. Publish verification_txt_host / verification_txt_value as a DNS TXT record first. To verify by file or meta tag instead, use the domain verification endpoint, which takes a method.

Upload an API spec

POST /scopes/{scope_id}/targets/api-spec · scope scopes:write · role admin

multipart/form-data with a single file field. Returns a target object with target_type of api_spec. Giving the agent a spec is the single highest-leverage thing you can do for an API target — it stops discovery being guesswork.

curl -X POST https://api.vulnix.dev/scopes/$SCOPE_ID/targets/api-spec \
  -H "Authorization: Bearer $VULNIX_TOKEN" \
  -F "file=@openapi.json"

Remove a target

DELETE /scopes/{scope_id}/targets/{target_id} · scope scopes:write · role admin

{ "status": "removed" }

Constraints

PATCH /scopes/{scope_id}/constraints · scope scopes:write · role admin

Sparse — send only what you are changing. Invalid combinations return 422 invalid_scope_constraints.

Prop

Type

curl -X PATCH https://api.vulnix.dev/scopes/$SCOPE_ID/constraints \
  -H "Authorization: Bearer $VULNIX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "excluded_paths": ["/admin/billing", "/webhooks"],
    "rate_limit_rps": 5,
    "testing_window_start": "02:00",
    "testing_window_end": "05:00",
    "testing_window_timezone": "Europe/Berlin"
  }'

Returns { id, status } plus every constraint field.

Context and documents

Context is what turns a generic scan into a targeted one. Give the agent what a new engineer on your team would need.

Set context text

PATCH /scopes/{scope_id}/context · scope scopes:write · role admin

{ "context_text": "Multi-tenant SaaS. Tenant id travels in the X-Org header, not the JWT." }

Returns { id, context_text }. Invalid content returns 422 invalid_scope_context.

Upload a document

POST /scopes/{scope_id}/documents · scope scopes:write · role admin

multipart/form-data with a single file field. Returns { id, filename, created_at }.

DELETE /scopes/{scope_id}/documents/{document_id} removes one, returning { "status": "removed" }.

Attack surface

Surfaces are hosts discovered around your targets. Classifying them is how you say what the agent may touch.

Prop

Type

Prop

Type

Add a surface

POST /scopes/{scope_id}/surfaces · scope scopes:write · role admin

Prop

Type

Classification is assigned for you — attackable for first-party, accessible for third-party. Change it with the endpoint below.

Reclassify a surface

PATCH /scopes/{scope_id}/surfaces/{surface_id} · scope scopes:write · role admin

{ "classification": "out_of_scope" }

Exclude every surface from one source

POST /scopes/{scope_id}/surfaces/exclude-all · scope scopes:write · role admin

{ "source": "third_party" }

Marks every surface from that source out_of_scope in one call. Returns { "status": "excluded" }. This is the safe default for third-party hosts when you have not reviewed them individually.

Toggle monitoring

PATCH /scopes/{scope_id}/scope-enabled · scope scopes:write · role admin

{ "enabled": true }

Returns { id, scope_enabled, surfaces }.

Source repository

POST /scopes/{scope_id}/repository · scope scopes:write · role admin

{ "repository_id": "01M2N…" }

Required before a whitebox run can start. Returns { id, repository_id }. An id that is not a connected repository returns 422 invalid_scope_repository.

Completion notifications

PATCH /scopes/{scope_id}/notify-on-completion · scope scopes:write · role admin

Prop

Type

Returns { id, notify_email_on_completion, notify_email_cc }.

On this page