CI/CD Integration

Add accessibility checks to your deployment pipeline. AllyProof can block or warn on pull requests that introduce WCAG violations, catching regressions before they reach production.

Generate an API Key

Go to Settings > API Keys and click Create Key. Give it a descriptive name (e.g. github-actions-ci) and copy the key immediately — it is shown only once.

Store the key as a secret in your CI environment:

  • GitHub Actions: Settings > Secrets > ALLYPROOF_API_KEY
  • GitLab CI: Settings > CI/CD > Variables > ALLYPROOF_API_KEY

The Scan Endpoint

Trigger a scan by sending a POST request to the scan API. The site must already be added and verified in your AllyProof dashboard.

HTTP
POST https://allyproof.com/api/v1/scan
x-api-key: <API_KEY>
Content-Type: application/json

{
  "url": "https://example.com",
  "threshold": 85,
  "wait": true,
  "max_pages": 20
}

Request parameters

FieldTypeRequiredDescription
urlstring (URL)YesThe verified site URL to scan.
thresholdnumber (0-100)NoMinimum score to pass. Default: 0 (always passes).
waitbooleanNoIf true, holds the connection until the scan completes (up to 5 min). Default: false.
max_pagesnumber (1-100)NoMaximum pages to scan. If omitted, scans all discovered pages up to the site's configured limit.

Note: The API key is passed via the x-api-key header, not the Authorization header.

Response Formats

Synchronous (wait: true)

When wait is true, the API holds the connection open until the scan completes and returns results directly:

JSON
{
  "pass": true,
  "score": 92,
  "threshold": 85,
  "scan_id": "scan-uuid",
  "site": { "name": "My Site", "url": "https://example.com" },
  "summary": {
    "pages_scanned": 12,
    "pages_failed": 0,
    "total_violations": 3,
    "critical": 0,
    "serious": 1,
    "moderate": 2,
    "minor": 0
  },
  "duration_ms": 14200,
  "top_violations": [
    {
      "rule_id": "color-contrast",
      "impact": "serious",
      "description": "Elements must meet minimum color contrast ratio thresholds",
      "pages_affected": 3
    }
  ],
  "pages": [
    { "url": "example.com/", "violations": 1, "critical": 0 },
    { "url": "example.com/about", "violations": 2, "critical": 0 }
  ],
  "dashboard_url": "https://allyproof.com/sites/site-uuid"
}

Asynchronous (default)

Without wait: true, the API returns immediately with a scan ID and a poll URL. The response has HTTP status 202 Accepted.

JSON
{
  "scan_id": "scan-uuid",
  "status": "pending",
  "poll_url": "/api/v1/scan?id=scan-uuid",
  "message": "Scan started. Poll the poll_url for results, or use wait=true to block."
}

To poll for results, hit the URL the response gave you with the same API key:

HTTP
GET https://allyproof.com/api/v1/scan?id=scan-uuid
x-api-key: <API_KEY>

Threshold Configuration

The threshold parameter sets the minimum acceptable accessibility score. When the scan completes:

  • If the score is at or above the threshold, the response includes "pass": true
  • If the score is below the threshold, the response includes "pass": false

Use this in your CI script to decide whether to pass or fail the pipeline step. A threshold of 85 is a reasonable starting point for most sites. Raise it gradually as you fix existing violations.

GitHub Actions Example

Save this as .github/workflows/accessibility.yml:

YAML
name: Accessibility Check

on:
  pull_request:
    branches: [main]

jobs:
  a11y-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Run AllyProof scan
        id: scan
        run: |
          RESPONSE=$(curl -s -X POST \
            https://allyproof.com/api/v1/scan \
            -H "x-api-key: ${{ secrets.ALLYPROOF_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{
              "url": "${{ vars.SITE_URL }}",
              "wait": true,
              "threshold": 85
            }')

          SCORE=$(echo "$RESPONSE" | jq -r '.score')
          PASS=$(echo "$RESPONSE" | jq -r '.pass')
          VIOLATIONS=$(echo "$RESPONSE" | jq -r '.summary.total_violations')
          URL=$(echo "$RESPONSE" | jq -r '.dashboard_url')

          echo "score=$SCORE" >> "$GITHUB_OUTPUT"
          echo "pass=$PASS" >> "$GITHUB_OUTPUT"
          echo "violations=$VIOLATIONS" >> "$GITHUB_OUTPUT"
          echo "url=$URL" >> "$GITHUB_OUTPUT"

      - name: Comment on PR
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const score = '${{ steps.scan.outputs.score }}';
            const violations = '${{ steps.scan.outputs.violations }}';
            const url = '${{ steps.scan.outputs.url }}';
            const passed = '${{ steps.scan.outputs.pass }}' === 'true';
            const emoji = passed ? '✅' : '❌';

            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: `${emoji} **AllyProof Scan**\n\nScore: **${score}/100** | Violations: **${violations}**\n\n[View full report](${url})`
            });

      - name: Fail if below threshold
        if: steps.scan.outputs.pass == 'false'
        run: |
          echo "Accessibility score ${{ steps.scan.outputs.score }} is below threshold"
          exit 1

GitLab CI Example

Save this as .gitlab-ci.yml:

YAML
accessibility:
  stage: test
  image: alpine:latest
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      RESPONSE=$(curl -s -X POST \
        https://allyproof.com/api/v1/scan \
        -H "x-api-key: $ALLYPROOF_API_KEY" \
        -H "Content-Type: application/json" \
        -d "{
          \"url\": \"$SITE_URL\",
          \"wait\": true,
          \"threshold\": 85
        }")

      SCORE=$(echo "$RESPONSE" | jq -r '.score')
      PASS=$(echo "$RESPONSE" | jq -r '.pass')

      echo "Accessibility Score: $SCORE/100"

      if [ "$PASS" = "false" ]; then
        echo "Score below threshold — failing pipeline"
        exit 1
      fi
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Best Practices

  • Run scans on pull requests, not just on merge to main — catch regressions early.
  • Start with a low threshold (e.g. 70) and raise it gradually as you fix existing violations.
  • Use max_pages to limit scan scope for faster CI runs.
  • Set up email notifications as a backup so stakeholders see results even if they don't check CI.
  • Use the dashboard_url in PR comments so developers can jump straight to the full report.

Limitations

  • Automated scanning covers approximately 57–70% of WCAG 2.2 AA criteria. Criteria that require human judgment (e.g., meaningful alt text, logical reading order) are not tested.
  • Scans with wait: true time out after 5 minutes. For large sites, use async mode and poll.
  • The site must be verified in your AllyProof dashboard before the API can scan it.