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