Vulnix
API

Domains

Register targets, prove you control them, store test-user credentials, and connect a DNS provider for one-click verification.

A domain is a host you have registered and proved you control. Nothing is tested until it is verified — that proof is what separates a pentest from an attack.

The domain object

Prop

Type

All four verification_* proof fields are precomputed and go null once the domain is verified — you never have to build the record strings yourself.

Register a domain

POST /domains · scope domains:write · role admin · returns 201

Prop

Type

curl -X POST https://api.vulnix.dev/domains \
  -H "Authorization: Bearer $VULNIX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target_type": "domain", "value": "acme-demo.com"}'
{
  "id": "01M2NMSF7GJY6KMMKZJ0EA0N47",
  "target_type": "domain",
  "value": "acme-demo.com",
  "verification_status": "pending",
  "verification_method": null,
  "verification_failure_reason": null,
  "dns_provider": null,
  "verification_txt_host": "_vulnix-verify.acme-demo.com",
  "verification_txt_value": "vulnix-verify=86b64bbb2b229da31a35e9f3f63253b7",
  "verification_file_path": "/.well-known/vulnix-verify-86b64bbb2b229da31a35e9f3f63253b7.txt",
  "verification_meta_tag": "<meta name=\"vulnix-verification\" content=\"86b64bbb2b229da31a35e9f3f63253b7\">",
  "issue_count": null,
  "last_tested_at": null
}

Verify a domain

POST /domains/{domain_id}/verify · scope domains:write · role admin

Publish one of the three proofs, then call this with the matching method.

Prop

Type

curl -X POST https://api.vulnix.dev/domains/$DOMAIN_ID/verify \
  -H "Authorization: Bearer $VULNIX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"method": "dns"}'

Returns the updated domain object. Verification is not an error when it fails — you get 200 with verification_status still pending and verification_failure_reason explaining why. Read the field; do not rely on the status code.

An unrecognized method returns 422 invalid_scope_target. A target type that cannot be DNS verified returns 422 domain_not_dns_verifiable.

DNS propagation

A freshly published TXT record can take minutes to become visible. Retry with backoff rather than treating the first pending as final.

List and read domains

GET /domains · scope domains:read · role admin

{ "domains": [] }

GET /domains/{domain_id} returns one domain object. While it is unverified this endpoint also performs a live nameserver lookup to populate dns_provider, so it is slightly slower than the list.

Delete a domain

DELETE /domains/{domain_id} · scope domains:write · role admin

{ "status": "deleted" }

A domain still referenced by a scope returns 409 domain_delete_blocked. Remove it from the scope's targets first.

Check reachability

POST /domains/check-reachability · scope domains:write · role admin

{ "value": "acme-demo.com" }
{ "reachable": true }

A pure probe — it registers nothing. Useful as a preflight before adding a host, so a typo fails fast instead of becoming a target that can never verify.

Findings and runs for a domain

GET /domains/{domain_id}/issues · scope domains:read · role admin

{ "issues": [] }

Holds finding objects.

GET /domains/{domain_id}/tests · scope domains:read · role admin

{ "tests": [] }

Holds run objects — every run that covered this domain.

Test users

Credentials the agent authenticates with. Without them, everything behind your login screen is untested — which is usually where the interesting findings are.

Prop

Type

Secrets are write-only

password and totp_secret are accepted on create and never returned by any endpoint. They are encrypted at rest and only ever decrypted inside the sandbox at run time. There is no way to read a stored credential back through the API — if you lose it, replace it.

Add a test user

POST /domains/{domain_id}/test-users · scope domains:write · role admin · returns 201

Prop

Type

List test users

GET /domains/{domain_id}/test-users · scope domains:read · role admin

{ "test_users": [] }

Only reusable ones appear here. A test user created with is_reusable: false is returned once by its create call — for the run you are about to start — and never listed again.

Remove a test user

DELETE /domains/{domain_id}/test-users/{test_user_id} · scope domains:write · role admin

{ "status": "removed" }

DNS provider connections

Connect Vercel or Cloudflare once and Vulnix can create the verification record itself.

List connections

GET /dns-connections · scope domains:read · role admin

{
  "connections": [
    {
      "provider": "cloudflare",
      "account_label": "acme-inc",
      "connected_by": "01K5ADM…",
      "connected_at": "2026-09-16T17:02:44.118293+00:00"
    }
  ]
}

Connect

POST /dns-connections/{provider}/start · scope domains:write · role admin

provider is vercel or cloudflare; anything else returns 422 unsupported_dns_provider.

{ "return_to": "https://your-app.example.com/settings" }
{ "authorize_url": "https://dash.cloudflare.com/oauth2/auth?…" }

This is an OAuth flow, so it needs a browser — send the user to authorize_url, then post the code and state they come back with to POST /dns-connections/{provider}/callback. A mismatched state returns 422 invalid_dns_connect_state.

Not automatable end-to-end

A human has to approve the OAuth grant. Connect the provider once through the console; after that, auto-verify below is fully scriptable.

Auto-verify

POST /domains/{domain_id}/dns-connections/{provider}/auto-verify · scope domains:write · role admin

Creates the TXT record through the connected provider and immediately re-runs the DNS check — one call instead of publish-then-verify. Returns the updated domain object. Failure to write the record returns 422 dns_record_creation_failed; no connection for that provider returns 404 dns_provider_connection_not_found.

Disconnect

POST /dns-connections/{provider}/disconnect · scope domains:write · role admin

{ "provider": "cloudflare" }

On this page