Testing
List test activities (organization)
Section titled “List test activities (organization)”GET /v1/organizations/:orgId/test-activityRequired role: Member.
Returns a paginated list of test activities across all non-archived products in the organization, including productName and productSlug fields.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
page |
integer |
Page number (default: 1). |
pageSize |
integer |
Results per page (default: 20, max: 100). |
type |
string |
Comma-separated list of one or more of automated_scan, manual_test, usability_test, user_research. Filter by activity type. |
productId |
string |
Filter to a specific product. |
sortField |
"createdAt" | "startedAt" | "completedAt" |
Field to sort by (default: "createdAt"). |
sortDirection |
"asc" | "desc" |
Sort direction (default: "desc"). |
Response:
{ "testActivities": [...], "pagination": { "page": 1, "pageSize": 20, "total": 42, "totalPages": 3 }}List test activities (product)
Section titled “List test activities (product)”GET /v1/organizations/:orgId/products/:productSlug/test-activityRequired role: Member.
Same query parameters as the organization-level list, except productId (fixed by the URL). Each item also includes areas.
Response: Same shape as the organization-level list.
Testing feed (product)
Section titled “Testing feed (product)”GET /v1/organizations/:orgId/products/:productSlug/testing-feedRequired role: Member.
Returns a merged, paginated, chronological stream of the product’s test activities and audits. Audits are included only when the organization also has the audits module enabled; the endpoint itself is gated on testing alone.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
page |
integer |
Page number (default: 1). |
pageSize |
integer |
Results per page (default: 20, max: 100). |
contextTrigger |
string |
Comma-separated triggers, or none for entries with no context. |
contextEnvironment |
string |
Comma-separated environment names, preview for pull request previews, or none for entries naming no environment. |
Both filters drop any entry that recorded no context unless the filter includes none — every audit, plus any activity logged by hand or reported without a context object.
Response:
{ "entries": [ { "kind": "test_activity", "id": "...", "number": 3, "type": "automated_scan", "tool": "axe", "title": null, "context": { "trigger": "pull_request", "ref": "fix/contrast", "commitSha": "0c4de91aa7712b35", "prNumber": 577, "prUrl": "https://github.com/acme/shop/pull/577", "environment": null, "runNumber": 1287, "runAttempt": 1 }, "externalUrl": "https://github.com/acme/shop/actions/runs/9001", "occurredAt": "2025-06-01T00:00:00.000Z", "discoveredIssueCount": 4, "discoveredFeedbackCount": 0 }, { "kind": "audit", "id": "...", "number": 2, "title": null, "status": "completed", "occurredAt": "2025-05-20T00:00:00.000Z", "discoveredIssueCount": 7 } ], "tools": ["axe", "manual"], "environments": ["production", "staging"], "hasPreviewEnvironments": true, "pagination": { "page": 1, "pageSize": 20, "total": 12, "totalPages": 1 }}occurredAt is a test activity’s completedAt (falling back to createdAt), or an audit’s completion/start date. Entries are sorted newest first.
Audit entries have no context field at all, since an audit records no run. Activity entries always carry one, null when the activity was logged by hand or reported without a context object. tools and environments are the values recorded in this scope, for building filter menus; they ignore the current query so every choice stays available. environments lists real deployments only — one recorded at least once outside a pull request — while hasPreviewEnvironments reports whether any pull request previews exist, since there is one per pull request and listing them would grow without bound.
Testing feed (organization)
Section titled “Testing feed (organization)”GET /v1/organizations/:orgId/testing-feedRequired role: Member.
Same as the product-level feed, but spans every non-archived product in the organization. Entries additionally include productName and productSlug.
Log a test activity
Section titled “Log a test activity”POST /v1/organizations/:orgId/products/:productSlug/test-activityRequired role: Member.
Cannot log an activity for an archived product — returns 409 Conflict.
Request body:
| Field | Type | Description |
|---|---|---|
type |
"automated_scan" | "manual_test" | "usability_test" | "user_research" |
Required. Kind of evaluative activity. |
title |
string |
Optional custom title (max 200 characters). Defaults to a generated title from the type and product name. |
tool |
string |
Optional producer slug or free text (max 120 characters). |
target |
string |
Optional URL(s) or scope tested (max 2000 characters). |
externalUrl |
string |
Optional URL linking to the source, e.g. a CI run. |
startedAt |
string (ISO 8601) |
Optional start time. |
completedAt |
string (ISO 8601) |
Optional completion time. Defaults to now. |
areaIds |
string[] |
Optional area IDs this activity covered. All must belong to the product. |
Activity numbers are sequential per product, assigned atomically. Logging an activity recomputes the product’s Grade.
Returns 201 Created with a Location header.
Get a test activity
Section titled “Get a test activity”GET /v1/organizations/:orgId/products/:productSlug/test-activity/:numberRequired role: Member.
:number is the sequential integer activity number, not a UUID.
List issues for a test activity
Section titled “List issues for a test activity”GET /v1/organizations/:orgId/products/:productSlug/test-activity/:number/issuesRequired role: Member.
Returns all issues linked to the specified test activity.
Delete a test activity
Section titled “Delete a test activity”DELETE /v1/organizations/:orgId/products/:productSlug/test-activity/:numberRequired role: Admin or owner.
Recomputes the product’s Grade. Returns 204 No Content.
Ingest a test activity report
Section titled “Ingest a test activity report”POST /api/ingest/test-activityPublic endpoint for external tools (the GitHub accessibility scanner is the first intended client). Unlike every other endpoint on this page, it is not under /v1 and is not session-authenticated — it’s mounted at the app level, like the webhook receiver, and authenticated by a bearer API key instead.
Authentication:
Authorization: Bearer <API key with the ingest scope>Returns 401 Unauthorized if the key is missing, invalid, or revoked, and 403 Forbidden if the key is valid but lacks the ingest scope. Create a key under Settings → API keys.
Rate limit: 300 requests / minute, keyed by client IP (not by key, so a rotated or invalid key can’t dodge the limit). This is separate from — and in addition to — the global rate limit, which does not apply to this path. Exceeding it returns 429 Too Many Requests.
Request body (max 1 MB, returns 413 Payload Too Large if exceeded):
| Field | Type | Description |
|---|---|---|
productRef |
string |
Product slug to attribute the activity to. Required unless the key is already scoped to a single product. |
type |
"automated_scan" | "manual_test" | "usability_test" | "user_research" |
Required. |
tool |
string |
Producer slug or free text (max 120 characters). Required for automated_scan, where it scopes both finding identity and auto-resolution. Optional otherwise. |
title |
string |
Optional title (max 200 characters). |
target |
string |
Optional URL(s) or scope tested (max 2000 characters). |
externalUrl |
string |
Optional URL linking to the source. |
startedAt |
string (ISO 8601) |
Optional start time. |
completedAt |
string (ISO 8601) |
Optional completion time. Defaults to now. |
idempotencyKey |
string |
Optional. Re-posting the same key for the same product returns the existing activity instead of creating a duplicate. |
context |
object |
Optional. The run the activity came from — see below. |
areas |
string[] |
Optional area slugs (max 50) to scope the activity to. All must exist on the target product. |
coverage |
"complete" | "partial" |
Whether the run examined its whole scope. Defaults to partial. Only complete lets Ablebase resolve findings the run no longer reports, and only when areas is omitted; see below. |
findings |
array |
Defaults to []. For automated_scan these become findings awaiting triage; for the human-run types they become issues directly. |
Context fields:
The context records three separate things: what started the run, which code it evaluated, and where it ran. Only the environment says whether an activity described your live product, so a scan of a pull request preview stays distinguishable from a scan of production.
| Field | Type | Description |
|---|---|---|
trigger |
"pull_request" | "push" | "schedule" | "manual" | "other" |
Required within context. What started the run. Use other rather than guessing when none fits. |
ref |
string |
Optional branch or tag name (max 255 characters). |
commitSha |
string |
Optional commit that was evaluated (max 64 characters). |
prNumber |
integer |
Optional pull request number. |
prUrl |
string |
Optional URL of the pull request. |
environment |
string |
Optional deployment the activity ran against, e.g. "production" (max 120 characters). Omit it for a CI job that scans a server it starts itself — that describes no deployment. |
runNumber |
integer |
Optional run number as your CI displays it. |
runAttempt |
integer |
Optional attempt number; 1 for a first run. A rerun is ingested as its own activity, so this is what tells two otherwise identical entries apart. |
Finding fields:
| Field | Type | Description |
|---|---|---|
title |
string |
Required (max 300 characters). |
description |
string |
Optional (max 5000 characters). |
severity |
"high" | "medium" | "low" |
Optional. |
impact |
"high" | "medium" | "low" |
Optional. |
sourceUrl |
string |
Optional URL. |
wcag |
string[] | string |
Optional WCAG success criteria, e.g. ["1.4.3"]. A rule often maps to several: axe’s video-caption covers 1.2.1, 1.2.2 and 1.2.3, so send them all. A bare string is still accepted and read as a one-element list. Each entry is trimmed and must be non-empty (max 50 characters, max 20 entries); omit the field rather than sending []. |
selector |
string |
Optional CSS selector of the failing element. Part of a finding’s identity, so send the same one on every run. |
ruleId |
string |
The scanner’s own stable rule identifier, e.g. axe-core’s "color-contrast" (max 200 characters). Required for automated_scan; optional otherwise. |
If the key is scoped to a product and productRef names a different one, returns 403 Forbidden. If the target product is archived, returns 409 Conflict.
Response:
{ "testActivity": { "id": "...", "number": 4 }, "issueCount": 0, "findingCount": 3, "resolvedCount": 1, "created": true}| Field | Description |
|---|---|
issueCount |
Issues created directly. Always 0 for automated_scan, which raises findings instead. |
findingCount |
Findings this run reported, whether newly raised or already known. |
resolvedCount |
Findings resolved because a complete sweep no longer reports them. Always 0 unless coverage is complete and the report omits areas. |
201 Created for a new activity, 200 OK when idempotencyKey matched an existing one (created: false; findings are not re-applied on replay).