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