Accessibility testing API, webhooks and CI pre-publish checks

The Reviseberg accessibility testing API brings findings into your own systems, webhooks let you react when a crawl finishes, and the pre-publish check lets your CI pipeline ask, before a release, whether a page meets your threshold. The findings are the same as in the app: axe-core rules on rendered pages, keyboard agent results and a WCAG 2.2 status for every criterion.

Three ways into your toolchain

Three ways into your toolchain
RouteWhat forAvailable
REST APIPull sites, open issues, score history and crawls into dashboards, ticketing or your own reportswith an API key: Starter 2, Growth 10 and Enterprise by agreement; none on the free account
Pre-publish checkTest a page of a preview or staging environment before release and fail the build when your threshold is missedpart of the REST API, same keys
WebhooksReact to finished crawls, new critical issues, score drops and outageson every account, added by an Admin

On top of that: the MCP server for coding agents, and CSV export. Enterprise also includes us wiring the pre-publish check into your CI with you.

REST API

The API speaks JSON at https://reviseberg.com/api/v1 and authenticates with an API key in the header Authorization: Bearer bz_…. An Admin creates the key in the app under Settings → API; it is shown exactly once, can be narrowed to particular sites and works until you revoke it.

REST API
EndpointWhat it returns
GET /api/v1/sitesThe sites the key can see: ID, name, address, page limit, crawl schedule, last crawl
GET /api/v1/issues?site=IDThe open issues from the latest completed crawl: rule, severity, WCAG level and criteria, occurrences, pages, points back and help link, plus a score
GET /api/v1/sites/ID/scoresThe score history across up to the last 90 completed crawls
GET /api/v1/sites/ID/crawlsRecent runs with status, trigger, start and finish time – so you can see whether a run is done
POST /api/v1/prepublishQueues the check of one page (see below)
GET /api/v1/prepublish/IDThe status and verdict of that check

Issues prefixed qa: are quality (broken links, for example), seo: are search; everything else, including the agent's keyboard: rules, is accessibility.

curl -s "https://reviseberg.com/api/v1/sites" \
  -H "Authorization: Bearer $REVISEBERG_API_KEY"

There is no separate API reference site yet; the table above is the complete list. The REST API does not start a crawl of the whole site – you do that in the app, on a schedule, or with the MCP server's run_scan tool.

A common use is creating tickets. There is no direct Jira, GitLab or GitHub Issues integration. With the API or a webhook you can build that bridge yourself – and decide which findings deserve a ticket.

Webhooks

Reviseberg sends an HTTP POST to an HTTPS address you choose when something happens. After every crawl exactly one message goes out, the most important one: crawl_failed, otherwise new_critical (new critical issues), otherwise score_drop (the score fell), otherwise scan_finished. Uptime monitoring adds uptime_down and uptime_up – once when a monitor goes down and once when it recovers, not at every check.

A webhook has one of two formats:

  • Slack format: {"text": "…"} – a line Slack and Microsoft Teams show as a message.
  • Generic: event, title, text, url (the link to the report), site, data and at. For a crawl, data holds the score and the number of open and critical issues.

The address itself is the credential – as with Slack. Requests are not signed, so treat the address like a password. Reviseberg only accepts https://, shows the address truncated in the app, delivers each message once with a ten-second timeout, and records a failure on the webhook so you can see why a channel went quiet.

Pre-publish checks in GitHub Actions and GitLab CI

The pre-publish check tests one page before you publish: axe-core at both widths, without the keyboard agent and without a link check. Your pipeline sends the address, gets a check ID and polls until the verdict is in:

  • POST https://reviseberg.com/api/v1/prepublish with {"url": "https://…"} answers 202 with check.id.
  • GET https://reviseberg.com/api/v1/prepublish/ID returns check.status (queued, running, done or failed) and, once it is done, the verdict (pass or fail), counts per severity, and the findings with rule, selector and message.
  • You set your threshold when you poll: minSeverity (the lowest severity that counts, default minor) and maxFailures (how many of those you tolerate, default 0).

The verdict is deliberately a count, not a score: a gate that lets a build through because the number was still 91 is a gate nobody trusts. Cases axe-core cannot decide are reported and never counted as failures.

The address must belong to a site in your account – the same host or a subdomain of it, such as staging.example.com for example.com. A preview on an entirely different host is a site you add. If your staging environment sits behind a login, store that login on the site (the browser's password prompt, a login form, token headers or a proxy); it is stored encrypted and only ever sent to that host. The crawler identifies as RevisebergBot and honours robots.txt – a staging site that disallows every crawler has to allow it.

The logic lives in one small script that both CI systems share:

#!/usr/bin/env bash
# scripts/reviseberg-check.sh – check one page before release
set -euo pipefail
: "${REVISEBERG_API_KEY:?}" "${PREVIEW_URL:?}"
API="https://reviseberg.com/api/v1"
AUTH="Authorization: Bearer $REVISEBERG_API_KEY"
MIN_SEVERITY="${MIN_SEVERITY:-serious}"   # count serious and critical only
MAX_FAILURES="${MAX_FAILURES:-0}"         # how many of those you tolerate

CHECK=$(curl -sf -X POST "$API/prepublish" -H "$AUTH" \
  -H "Content-Type: application/json" \
  -d "{\"url\":\"$PREVIEW_URL\"}" | jq -r '.check.id')

for i in $(seq 1 60); do                  # up to 10 minutes
  RESULT=$(curl -sf "$API/prepublish/$CHECK?minSeverity=$MIN_SEVERITY&maxFailures=$MAX_FAILURES" -H "$AUTH")
  STATUS=$(jq -r '.check.status' <<<"$RESULT")
  if [ "$STATUS" = "done" ] || [ "$STATUS" = "failed" ]; then break; fi
  sleep 10
done

jq -r '.findings[]? | "\(.severity)\t\(.rule)\t\(.selector)"' <<<"$RESULT"
echo "Status: $STATUS · verdict: $(jq -r '.verdict' <<<"$RESULT")"
if [ "$(jq -r '.verdict' <<<"$RESULT")" != "pass" ]; then
  echo "::error::Accessibility threshold not met for $PREVIEW_URL"; exit 1
fi

GitHub Actions (.github/workflows/accessibility.yml):

name: Accessibility before release
on:
  pull_request:
  workflow_dispatch:
jobs:
  reviseberg:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Reviseberg pre-publish check
        run: bash scripts/reviseberg-check.sh
        env:
          REVISEBERG_API_KEY: ${{ secrets.REVISEBERG_API_KEY }}
          PREVIEW_URL: "https://staging.example.com/checkout/"
          MIN_SEVERITY: "serious"

GitLab CI (.gitlab-ci.yml):

reviseberg:
  stage: test
  image: alpine:3.20
  before_script:
    - apk add --no-cache bash curl jq
  script:
    - bash scripts/reviseberg-check.sh
  variables:
    PREVIEW_URL: "https://staging.example.com/checkout/"
    MIN_SEVERITY: "serious"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Store the API key as an encrypted secret (GitHub) or a masked CI/CD variable (GitLab) – never in the repository. Each call checks one page; for several – home, product page, checkout – run the script once per page.

Thresholds that work

You set the threshold, not us. Three patterns work well:

  1. Let nothing serious through: MIN_SEVERITY=serious, MAX_FAILURES=0. The build fails on any serious or critical finding on the page checked. A good start for the pages that carry revenue.
  2. A ratchet: set MAX_FAILURES to today's count and lower it after every improvement, so it can only move one way.
  3. Zero tolerance for specific rules: read the findings list yourself and fail the build on particular rules, such as missing form labels (label) – barriers that lock people out completely.

The check does not compare against your live site; it counts what it finds on the page it checked. A passing check also does not mean the page conforms. It means the automated checks found nothing that breaks your threshold. Criteria that need human judgement stay "untested" – see our methodology.

axe-core in your build and Reviseberg: both

Component tests with axe-core (in Playwright or Jest, say) catch problems early, while you build. But they only see the states your tests create. Reviseberg tests the finished, rendered site across many pages, lets the keyboard agent walk your journeys, and keeps history, WCAG status and your accessibility statement up to date. The two complement each other.

Talk to us about wiring it into your CI

Frequently asked questions

Which CI systems are supported?

Any system that can run a shell script with curl and jq. Examples for GitHub Actions and GitLab CI are above; Jenkins, Bitbucket Pipelines or Azure DevOps follow the same pattern.

Does the staging environment have to be public?

Our crawler has to be able to reach it. A login, the browser's password prompt, token headers or a proxy can be stored on the site. The crawler does not join a VPN itself; the proxy option inside your network is for that.

How long does the check take?

It checks one page and is queued ahead of weekly crawls. How long it waits for a free worker depends on load; the script above waits up to ten minutes.

Is there a Jira or GitHub Issues integration?

No. You can create tickets today via the API and webhooks.

Which plans include the API?

Every plan with API keys: Starter 2, Growth 10 and Enterprise by agreement; none on the free account. The pre-publish check is part of the API. Webhooks need no key.

Sources

  1. GitHub Docs: workflow syntax for GitHub Actions – https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions
  2. GitLab Docs: CI/CD YAML syntax – https://docs.gitlab.com/ci/yaml/
  3. Deque: axe-core – https://github.com/dequelabs/axe-core
  4. W3C: WCAG 2.2 – https://www.w3.org/TR/WCAG22/

Talk it through with us

Thirty minutes on your site, your deadlines and what the product can and cannot settle for you.