Matter SolutionsBangBangAPI documentation · public Sign in

API docs

Everything the dashboard does is available over JSON, so any pipeline or service can use BangBang as its testing ground.

OpenAPI 3.1

Authentication

Every endpoint except /api/health needs an API key in every request: Authorization: Bearer sc_…. There are two kinds. An external app key is issued by a BangBang super admin on External apps and works only from that app's own domains and IP addresses; ask your Matter Solutions contact for one. A personal token is created by a super admin under API access. A key acts as its owner: customers reach only their own runs and sites, admins and super admins reach everything. This page and the OpenAPI document are public, so you can share their links with anyone; they never contain a key.

Base URL: https://bangbang.mattersolutions.co. Requests and responses are JSON unless you download an export. Runs are asynchronous: start one, watch /api/jobs or the event stream, then read results.

Test types

8 types; a run does all of them unless you choose
NameWhat it checks
performanceCore Web Vitals in Chromium per device (LCP, CLS, INP, TTFB), page weight, images, caching, Lighthouse (lighthouse: true), CrUX field data when a key is set, load test (load: true).
securitySecurity headers, TLS, cookies, exposed files and secrets, vulnerable libraries, XSS and SQL-injection signals, DNS and email records, platform leaks. Passive unless active: true.
seoTitles, descriptions, canonicals, robots, headings, structured data, hreflang, sitemap and robots.txt, thin or duplicate content.
accessibilityaxe-core (WCAG 2.x) on one device per class: contrast, labels, landmarks, alt text, ARIA.
responsiveLayout on every chosen device: horizontal scroll, overlaps, tap targets, text size; one screenshot per device.
contentCompare and before/after runs only: text, visual (pixel diff), missing pages, API and asset differences.
runtimeHTTP status and redirects, console errors, failed requests, broken links, user journeys (flows).
privacyBefore the consent click, on one device per page: requests to a curated list of tracker hosts, tracking cookies, the consent banner (accept vs reject in its first layer) and a privacy policy link. Not legal advice.

Run only some tests

Send tests when starting a run: "all" (the default) or a list of the names above. Only those tests run (faster), are scored and get report tabs; an unknown name is 400 with the valid list. The same field works on POST /v1/runs, schedules and POST /v1/runs/preview. GET /v1/tests returns this list as JSON (public).

curl -s -X POST "https://bangbang.mattersolutions.co/v1/sites/www.example.com/runs" \
  -H "Authorization: Bearer $BANGBANG_KEY" -H "content-type: application/json" \
  -d '{"url":"https://www.example.com/","tests":["performance","seo"],"lighthouse":true}'
# 202 {"id":"<run id>", ...}   all tests: "tests":"all" or leave it out

Get one test type's results and files

GET /v1/runs/{id}/tests lists every type in the run: whether it was selected, its scores and finding counts. GET /v1/runs/{id}/tests/{test} returns that type's scores, findings and the evidence per page, environment and device: performance gives Web Vitals and Lighthouse samples (plus CrUX and load results), security the response headers, cookies and TLS, seo the title, meta tags and headings, accessibility the axe violations, responsive the device screenshots and layout issues, content the screenshots and diff images, runtime the status, redirects, console errors and failed requests. Screenshots and diff images are artifact URLs the same key downloads. To get findings only, add test= to /v1/runs/{id}/findings or /v1/sites/{site}/findings.

curl -s -H "Authorization: Bearer $BANGBANG_KEY" "https://bangbang.mattersolutions.co/v1/runs/<run id>/tests"
curl -s -H "Authorization: Bearer $BANGBANG_KEY" "https://bangbang.mattersolutions.co/v1/runs/<run id>/tests/performance"
curl -s -H "Authorization: Bearer $BANGBANG_KEY" "https://bangbang.mattersolutions.co/v1/sites/www.example.com/findings?test=seo,accessibility&severity=medium"
curl -s -H "Authorization: Bearer $BANGBANG_KEY" "https://bangbang.mattersolutions.co/v1/sites/www.example.com/performance?form_factor=mobile"   # Lighthouse history
curl -s -H "Authorization: Bearer $BANGBANG_KEY" -o shot.png "https://bangbang.mattersolutions.co/v1/runs/<run id>/artifacts/shots/…png"  # a file from the response

Endpoints

availability monitoring (/api/monitors, /api/status-page, /status/…) is no longer served here; it lives in the separate Uptime app
MethodPathWhat it does
GET/api/healthLiveness, database status and running job count. Public.
POST/api/runsStart a run. Returns 202 with the run id; work continues in the background. tests limits it to some of performance, security, seo, accessibility, responsive, content, runtime (default "all"): the others do not run and are not scored.
{"mode":"audit"|"compare","urls":[…],"tests":"all"|["seo","security"],"names":[…],"tags":[…],"devices":"quick|standard|full|none","maxPages":30,"crawl":false,"against":"latest|<runId>","screenshots":true,"a11y":true,"dns":false,"sensitivePaths":true,"lighthouse":false,"lighthousePages":2,"load":false,"loadProfile":"smoke","active":false,"ownTarget":false,"credentialId":12,"callbackUrl":"https://…","callbackSecret":"…"}
GET/v1/sitesEvery site (project) you can see: base URL, last run, average score, verdict, finding counts, schedules. A site is addressed by its hostname or URL-encoded URL; www.example.com, example.com and https%3A%2F%2Fexample.com%2F are the same site.
GET/v1/sites/lookup?url=https://…Resolve a URL to its site key. 404 when it has no runs, settings or schedules yet; the body still carries project and site so your app can go on to create them.
GET/v1/sites/{site}Site summary: latest run and scores, score trend, open findings by severity, settings (secrets masked), schedules and active jobs.
GET/v1/sites/{site}/runs?limit=&cursor=&status=&mode=Runs of the site, newest first; nextCursor pages. /runs/latest is the latest finished run.
POST/v1/sites/{site}/runsStart an audit of the site (default URL: the site's last tested base URL). Same options, validation, scopes, Idempotency-Key, limits and callbacks as POST /api/runs; url must be on the site.
{"url":"https://www.example.com/shop","devices":"quick","lighthouse":true,"credentialId":12,"callbackUrl":"https://…"}
GET/v1/sites/{site}/findings?severity=high&status=open|new|allFindings of the latest finished run (or ?run=). severity=high means high and above; also test (one or more test types), category, ruleId, q, limit, cursor.
GET/v1/sites/{site}/performance?form_factor=mobile|desktop&page=/&limit=50Lighthouse performance over time: series (oldest first) of category scores and lab metrics, latest, and change against the previous sample of the same page. Needs runs with lighthouse: true.
PATCH/v1/sites/{site}/settingsMerge into the site's settings (nested objects merge, null removes, arrays replace). GET reads them masked, PUT replaces, DELETE clears.
{"defaults":{"maxPages":25,"lighthouse":true},"credentialId":12}
POST/v1/sites/{site}/schedulesRepeat a run of the site; GET lists them. PATCH /v1/schedules/{id} changes name, frequency, hour, request (merged) or enabled; DELETE removes it. A schedule's credentialId is used by every scheduled run.
{"name":"nightly","frequency":"daily","hourUtc":2,"request":{"devices":"quick","credentialId":12}}
POST/v1/sites/{site}/ignoresIgnore a finding or a rule for the site; GET lists ignores, DELETE ?ids= removes them. /v1/sites/{site}/accepted does the same for baseline-accepted fingerprints.
{"fingerprint":"…","scope":"finding","reason":"known, fixed next sprint","until":"2026-12-31"}
PATCH/v1/runs/{id}Notes, tags and visibility of a run in one call (visibility needs the shares:write scope).
{"notes":"…","tags":["release-42"],"visibility":"public"}
GET/api/runs?project=Your runs, newest first, optionally for one project.
GET/api/runs/{id}One run: status, counts per severity, scores, environments, timing.
GET/api/runs/{id}/findings?severity=high,critical&category=security&test=seoFindings with evidence, remediation and stable fingerprints. test keeps those test types (comma list).
GET/v1/testsThe test types a run can be limited to, with what each checks. Public. See Test types.
GET/v1/runs/{id}/testsEvery test type in the run: selected or not, its scores, finding counts by severity, and the URL of its detail.
GET/v1/runs/{id}/tests/{test}One test type: scores, findings, and the data and files it was judged on, per page, environment and device.
GET/api/runs/{id}/regressionNew, resolved and persisting findings against your previous run of the same project.
GET/api/runs/{id}/briefPlain-text client brief: what was tested, the verdict, what to do first, links. Ready to paste into an email.
GET/api/runs/{id}/export/{format}Download: json, findings, md, html, sarif (GitHub code scanning), junit (CI test report), pdf, zip (self-contained bundle).
DELETE/api/runs/{id}Delete a run and its stored findings.
GET/api/previous-runs?url=Earlier finished runs of a site, to pick a before/after base.
GET/api/jobsJobs this server process knows about, with live progress.
GET/api/jobs/{id}/eventsServer-sent events: progress, done, failed.
POST/api/baseline/acceptAccept expected findings so they stop appearing.
{"project":"…","fingerprints":["…"],"reason":"…","runId":"…"}
POST/api/baseline/removeRemove findings from the baseline.
{"project":"…","fingerprints":["…"]}
POST/api/baseline/ignoreIgnore a finding, a rule on a page or a rule project-wide, with a reason and an optional expiry. Ignored findings are not counted or scored.
{"project":"…","fingerprint":"…","scope":"finding|rule-page|rule","reason":"…","until":"2026-12-31","runId":"…"}
POST/api/baseline/unignoreRemove ignores by id, fingerprint or rule id.
{"project":"…","ids":["…"]}
GET/api/baseline?project=…Accepted and ignored findings of a project; ignores carry an active flag.
POST/api/runs/previewValidate a run request and see the resolved configuration (project, environments, pages, devices, checks) without starting anything. Same body as POST /api/runs.
PUT/api/runs/{id}/metaSet notes and tags on a run. Tags are lower-cased and limited to 12.
{"notes":"…","tags":["release-42","client-review"]}
POST/api/runs/{id}/visibilityLegacy public/private flag, admins and super admins only. It no longer opens a run to other users: customers only ever see their own runs. To show a run to someone, create a share link.
{"public":true}
GET/api/runs/{id}/sharesPublic share links of a finished run. POST creates one (optional expiry in days, optional password of 6+ characters, audience, and sections: "all" or a list of overview, performance, security, seo, accessibility, responsive, content, runtime, server; server needs the internal audience). Anything outside a link's sections is unreachable through it; API tokens always read everything. PATCH /api/shares/{id} changes sections, expiry, audience or password and keeps the URL; DELETE /api/shares/{id} revokes it.
{"expiresDays":30,"audience":"external","sections":["overview","performance"]}
POST/api/runs/{id}/public-linkThe run's one public report link: anyone with it can open the report without signing in (external view, every tab). Reused when it exists; send password or expiresDays to get a separate link.
{"password":"optional, 6+ characters","expiresDays":30}
GET/api/runs/{id}/findings/{fingerprint}/commentsDiscussion of one finding, including comments left on the same finding in earlier runs. POST adds a comment; decision accept or ignore also accepts or ignores the finding. DELETE /api/comments/{id} removes a comment (its author or a super admin).
{"body":"Fixed in release 42","decision":"accept"}
GET/api/projects/{project}/history.csvOne row per run you can see for the project: status, counts per severity, every score, tags and notes, oldest first.
GET/api/projects/{project}/settingsPer-project run settings (sign-in credentials, headers, cookies, paths to skip). PUT saves them; secrets come back masked and stay unchanged when you send the mask back. DELETE clears them. GET /api/project-settings?url= resolves the project from a URL first.
GET/api/schedulesYour schedules. POST creates one that repeats a run request; POST /api/schedules/{id}/toggle pauses or resumes, POST /api/schedules/{id}/run-now starts it immediately, DELETE /api/schedules/{id} removes it.
{"name":"…","frequency":"hourly"|"every6h"|"daily"|"weekly","hourUtc":7,"request":{ …same body as POST /api/runs… }}
GET/api/notify-targetsWhere finished runs are announced: Slack, Teams, a JSON webhook or email. POST adds one; POST /api/notify-targets/{id}/test sends a sample; POST /api/notify-targets/{id}/toggle and DELETE /api/notify-targets/{id} pause or remove it.
{"kind":"slack"|"teams"|"webhook"|"email","target":"https://… or name@example.com","label":"…","minSeverity":"medium","onlyNew":false}
GET/api/digestYour weekly digest settings. PUT saves them; POST /api/digest/send-now emails it immediately; GET /api/digest/preview returns the HTML.
{"enabled":true,"weekday":1,"hourUtc":7}
GET/api/activityEverything this server process is doing: jobs with progress, the next scheduled runs, slots in use, process uptime and memory.
GET/api/tokensSuper admins only: your tokens (name, prefix, last use); ?all=1 lists every user's. POST creates one from a browser session; DELETE /api/tokens/{id} revokes any token. Others get 403.
GET/api/auth/meThe signed-in user behind the cookie or token.
GET/api/admin/overviewAdministrators: every user with their runs, schedules, tokens and alert targets, plus totals.
GET/api/admin/retentionAdministrators: retention policy, disk used by report files, and how many runs are due for file removal. POST /api/admin/prune removes them now.
GET/api/admin/appsSuper admins: external apps, each with its own shared key. An app's key works only from that app's domains, IP addresses and ranges, and only while the app is enabled; general keys work from any listed address once there is one. Refused calls get 403 with code app_not_allowed. Copy a key on External apps (Show / Copy). POST creates an app and returns its key once; GET|PATCH|DELETE /api/admin/apps/{id}; POST /api/admin/apps/{id}/entries adds an address; POST|GET|DELETE /api/admin/apps/{id}/key generates or rotates, reveals (logged) or revokes the key; /api/admin/apps/entries manages every entry; GET /api/admin/apps/check?ip=&origin=&appId= tests an address.

Limits

Requests to /api/* are counted per user (per client address when sign-in is off). Over the limit the API answers 429 with Retry-After; every metered response carries X-RateLimit-Limit and X-RateLimit-Remaining. Run starts have their own hourly budget. Health, this page and the OpenAPI document are exempt.

Errors, tokens and browsers

Every error is JSON: {"error":"…","code":"…"} with 400 invalid_request, 401 unauthorized (missing or bad token), 403 forbidden (scope, IP or host allowlist), 403 app_not_allowed (the key was used from an address that is not on its app's list), 404 not_found, 409 conflict, 429 rate_limited. A token with a host allowlist sees only those sites: other sites and their runs answer 404. Browser apps on another origin can call the API with a bearer token when the origin is listed in BANGBANG_CORS_ORIGINS or is an app domain with browser access; cookies are never accepted cross-origin.

Use in CI

gate a deployment on the findings of a fresh run
# GitHub Actions step: fail the build on new high or critical findings
- name: BangBang audit
  env:
    SC: https://bangbang.mattersolutions.co
    TOKEN: ${{ secrets.BANGBANG_TOKEN }}
  run: |
    id=$(curl -sf -X POST "$SC/api/runs" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" \
      -d '{"mode":"audit","urls":["https://staging.example.com"],"devices":"quick","maxPages":20}' | jq -r .id)
    until [ "$(curl -sf -H "Authorization: Bearer $TOKEN" "$SC/api/jobs" | jq -r --arg id "$id" '.[]|select(.id==$id)|.status')" != "running" ]; do sleep 10; done
    curl -sf -H "Authorization: Bearer $TOKEN" "$SC/api/runs/$id/export/sarif" -o results.sarif
    new=$(curl -sf -H "Authorization: Bearer $TOKEN" "$SC/api/runs/$id/regression" | jq '[.added[]|select(.severity=="high" or .severity=="critical")]|length')
    echo "new high/critical findings: $new"; [ "$new" = "0" ]
- uses: github/codeql-action/upload-sarif@v3
  with: { sarif_file: results.sarif }

Modes

audit
One URL. Every check on every page; screenshots per device are kept.
compare
Two URLs. Content, visual, HTTP and runtime differences plus all single-site checks on both.
before-after
Audit with against. Diffs the live site against one of your earlier runs.