API reference

Scans

Start a scan, follow it, read its problems, and stop it.

Start a scan, follow it, read its problems, and stop it. Paths are under https://www.valiqai.com/api/v1; see the API overview for keys and scopes.

POST /scans

Starts a scan. Scope scan.

Terminal
$ curl -X POST https://www.valiqai.com/api/v1/scans \    -H "Authorization: Bearer $VALIQAI_API_KEY" \    -H "Content-Type: application/json" \    -H "Idempotency-Key: deploy-4817-attempt-1" \    -d '{"url": "https://staging.example.com", "type": "e2e"}'
FieldTypeWhat it is
urlstringThe address to scan. Its domain must be verified (for a team key, by anyone on the team).
typee2e or securityThe scan: End-to-End, or Security, which also tests the site's chatbot. See Choose a scan.
chatbotConsentbooleanWith security: true says you're allowed to send the site's chatbot attack prompts. Without it, the chatbot is found but left alone.
organizationIdstringThe workspace to bill. A personal key in more than one workspace needs it. A team key can only name its own team.
sourceobjectWhere the scan was started from, shown on its page: provider (required, like github), repository, branch, commit, pullRequest, runUrl, label. The CLI fills it in from your CI.
labelstringA note shown with the scan, up to 120 characters.
webhookstringAn address to POST the scan's result to when it ends. See Per-scan callbacks.
mode, contractIdstringmode: "custom" with a contractId runs one of your contracts instead of the URL.

Send an Idempotency-Key header so a retried request returns the scan it already started.

Response 200:

JSON
{
  "id": "cmh2x8k1p0",
  "status": "pending",
  "url": "https://staging.example.com",
  "mode": "generic",
  "type": "e2e",
  "credits": { "reserved": 2, "free": false },
  "message": "Scan started",
  "pollUrl": "/api/v1/scans/cmh2x8k1p0",
  "badgeUrl": "/api/v1/scans/cmh2x8k1p0/badge",
  "runUrl": "https://www.valiqai.com/runs/cmh2x8k1p0"
}
  • credits.reserved is what the scan holds; what it doesn't use comes back when it ends. credits.free is true when your free End-to-End scan paid for it.
  • note appears when a security request runs as End-to-End because chatbot scans aren't available yet.

Before anything is held, a scan can be refused: the domain isn't verified (403 DOMAIN_NOT_VERIFIED), the site is behind protection (422 SITE_PROTECTED), the key's monthly limit is reached (402 KEY_CREDIT_LIMIT), there aren't enough credits (402 INSUFFICIENT_CREDITS), or another scan is running in the workspace (429 ORG_IN_FLIGHT_CAP, with Retry-After). All are on Errors.

GET /scans

Lists scans, newest first. Scope read. A team key lists its team's scans; a personal key, the ones you started.

QueryWhat it does
siteOnly scans of this domain and its subdomains, like example.com.
statusOnly these, comma-separated: pending, running, passed, failed, warning, error, partial, cancelled.
sourceOnly scans started from this provider, like github.
limitHow many, 1 to 100. Default 20.
cursorThe next page: nextCursor from the last response.
organizationIdOnly scans in this workspace.
JSON
{
  "scans": [
    {
      "id": "cmh2x8k1p0",
      "url": "https://staging.example.com",
      "status": "failed",
      "type": "e2e",
      "score": 64,
      "problems": { "open": 3, "blocker": 1, "major": 2, "minor": 0, "dismissed": 0 },
      "source": { "provider": "github", "branch": "main", "commit": "a1b2c3d" },
      "startedAt": "2026-10-20T09:12:04.000Z",
      "completedAt": "2026-10-20T09:15:16.000Z",
      "duration": 192000,
      "runUrl": "https://www.valiqai.com/runs/cmh2x8k1p0"
    }
  ],
  "total": 57,
  "nextCursor": "bzoyMA"
}

score is null while a scan runs and for a stopped one. problems counts open problems the way the Scans page does: major is High and minor is Medium.

GET /scans/{id}

A scan's status and, once it ends, its results. Scope read. A scan the key can't reach answers 404 Scan not found.

While it runs (pending or running), it has progress:

JSON
{ "progress": { "tests": { "total": 18, "running": 2, "done": 12, "failed": 1 }, "problemsSoFar": 2, "elapsedMs": 180000 } }

tests is null until the scan lists them. Once it ends, the scan has:

FieldWhat it is
statuspassed, failed, warning, error, partial or cancelled.
score, scoreBreakdownThe score out of 100, and its parts.
problemsOne per distinct problem, worst first: key, severity (blocker, major, minor, info), category, title, count, urls, status (open or dismissed), dismissReason.
openCounts, dismissedCountOpen problems by severity, and how many were dismissed for the site. Problems dismissed in ValiqAI don't count as open.
testsEach test the scan ran: id, section, name, state, result (passed, failed, inconclusive), reason, durationMs, findings.
findings, findingsCountThe raw findings behind the problems, each with dismissed.
type, source, riskSummary, coverageThe scan type, where it was started from, a risk summary, and what it covered.
runUrl, startedAt, completedAt, duration, errorIts page, times, and what went wrong if it didn't finish.

Polling every 10 seconds is plenty; that's what the CLI does.

DELETE /scans/{id}

Stops a scan that's still running, and releases its unused credits. Scope scan.

JSON
{ "id": "cmh2x8k1p0", "status": "cancelled", "runUrl": "https://www.valiqai.com/runs/cmh2x8k1p0" }

A scan that already ended answers 409 This scan already finished.

GET /scans/{id}/problems

A scan's problems, filtered, worst first. Scope read.

QueryWhat it does
severityComma-separated: blocker, high (or major), medium (or minor), info (or low).
categoryComma-separated, like security,accessibility.
statusopen (the default), dismissed or all.
JSON
{ "id": "cmh2x8k1p0", "url": "https://staging.example.com", "status": "failed", "problems": [ … ], "openCounts": { "blocker": 1, "major": 2, "minor": 0, "info": 0 }, "dismissedCount": 0 }

GET /scans/quote

What a scan would reserve, and what the workspace has, before starting it. Scope read.

QueryWhat it does
typee2e (the default) or security.
organizationIdThe workspace, as for POST /scans.
JSON
{
  "type": "e2e",
  "requestedType": "e2e",
  "sku": "quick",
  "label": "End-to-End scan",
  "hold": 2,
  "typicalMin": 1,
  "typicalMax": 1,
  "available": 140,
  "freeEligible": false,
  "workspace": { "id": "org_8f2…", "name": "Northstar" }
}

type is what it would actually run as, and note says so when that differs from requestedType.

GET /scans/{id}/badge

A status badge for a README, as SVG: the score once the scan ends, or its status. ?style=risk shows the risk level instead. It needs no key, so anyone with the scan's id can see it.

Code
![ValiqAI](https://www.valiqai.com/api/v1/scans/cmh2x8k1p0/badge)