Scanverra
CI/CD Integration

Guides

CI/CD Integration

Every scanner endpoint accepts an API key, so any of them can run as a pipeline step. This page covers what each tool's output means, how to turn it into a pass/fail gate, and working examples for four CI platforms.

Scoring basis per tool

What each endpoint returns, and what to threshold on.

ToolOutputBasisBuilt-in gate
Website Audit4 scores 0-100: performance, seo, accessibility, bestPracticesRaw Lighthouse scores. Convention: ≥90 good, 50-89 needs work, <50 poor.None built in — pick your own threshold per score.
Security ScanriskScore 0-100 (higher = safer)100 minus severity-weighted, category-capped deductions. Grade bands: A+ ≥90 … F <30.None built in — pick your own threshold.
Browser TestArrays: jsErrors, brokenLinks, formIssues, imgIssues (each severity: "error" | "warning")No aggregate score.Gate on issue counts, e.g. any severity === "error".
Repo Scannerscore 0-100 + qualityGate: "passed" | "failed"100 minus severity-weighted deductions, capped per category.Built in — auto-fails on any critical finding or score < 60.

Stopping the pipeline

CI mechanics are the same regardless of platform: a step that exits non-zero fails the job. To actually block a PR or merge request from merging, add that job as a required/status checkin your repo host's branch protection or merge-request approval settings — a failed step alone just shows red, it doesn't stop a merge on its own.

Example pipelines

The same four checks — Audit, Security Scan, Browser Test, Repo Scan — written for four platforms. Pick your CI system below.

name: Scanverra checks
on: [pull_request]

jobs:
  scanverra:
    runs-on: ubuntu-latest
    env:
      API_KEY: ${{ secrets.SCANVERRA_API_KEY }}
      TARGET_URL: https://staging.example.com   # your deployed preview URL
    steps:
      - name: Website Audit (perf/SEO/a11y)
        run: |
          RES=$(curl -sf -X POST https://www.scanverra.com/api/audit \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d "{\"url\":\"$TARGET_URL\"}")
          echo "$RES" | jq .
          PERF=$(echo "$RES" | jq '.scores.performance')
          A11Y=$(echo "$RES" | jq '.scores.accessibility')
          if (( $(echo "$PERF < 50" | bc -l) )) || (( $(echo "$A11Y < 90" | bc -l) )); then
            echo "::error::Audit failed thresholds (perf=$PERF, a11y=$A11Y)"
            exit 1
          fi

      - name: Security Scan
        run: |
          RES=$(curl -sf -X POST https://www.scanverra.com/api/security-scan \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d "{\"url\":\"$TARGET_URL\"}")
          RISK=$(echo "$RES" | jq '.riskScore')
          echo "riskScore=$RISK"
          [ "$RISK" -lt 70 ] && { echo "::error::Security risk score too low ($RISK)"; exit 1; }
          true

      - name: Browser Test (JS errors, broken links)
        run: |
          RES=$(curl -sf -X POST https://www.scanverra.com/api/browser-test \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d "{\"url\":\"$TARGET_URL\"}")
          ERRORS=$(echo "$RES" | jq '[.jsErrors[]? | select(.severity=="error")] | length')
          [ "$ERRORS" -gt 0 ] && { echo "::error::$ERRORS JS errors found"; exit 1; }
          true

      - name: Repo Scan (SAST, secrets, deps, quality gate)
        run: |
          SCAN_ID=$(curl -sf -X POST https://www.scanverra.com/api/repo/scan \
            -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
            -d '{"owner":"your-org","repo":"your-repo","branch":"${{ github.head_ref }}"}' | jq -r .scanId)

          for i in $(seq 1 30); do
            RESULT=$(curl -sf "https://www.scanverra.com/api/repo/result?id=$SCAN_ID" -H "X-API-Key: $API_KEY")
            STATUS=$(echo "$RESULT" | jq -r .status)
            [ "$STATUS" != "running" ] && break
            sleep 10
          done

          echo "$RESULT" | jq '{score, qualityGate}'
          GATE=$(echo "$RESULT" | jq -r .qualityGate)
          [ "$GATE" = "failed" ] && { echo "::error::Repo quality gate failed"; exit 1; }
          true

Where to store the API key as a secret, per platform:

  • GitHub Actions: repo secret — Settings → Secrets and variables → Actions.
  • GitLab CI: masked CI/CD variable — Settings → CI/CD → Variables.
  • Azure Pipelines: secret pipeline variable (lock icon) or a Variable Group under Pipelines → Library.
  • Jenkins: Secret text credential — Manage Jenkins → Credentials — referenced via credentials(). The agent image also needs curl, jq, and bc installed; GitHub-hosted and Azure-hosted ubuntu-latest runners already include them, GitLab's alpine image doesn't (hence the apk add step).

The Repo Scanner step needs your GitHub/Bitbucket/Azure account connected in Scanverra settings beforehand — it scans through the token stored there, not one passed per-request.

Quota note: a Free-plan API key is quota-limited the same as a Free web account — CI runs still count against your monthly scan limit and can exhaust it fast if you scan on every PR. Pro, Team, and Enterprise API keys are unmetered. See Limits & Behavior.

Only the Repo Scanner returns a ready-made qualityGatefield. For the other three tools, decide your own thresholds up front and keep them consistent across environments (e.g. don't compare a staging URL's security score against a threshold tuned for production).