Reference
Last updated 28 October 2026 · API version v1
Every endpoint on one page, so your browser’s own search reaches all of it. Each entry is generated from the OpenAPI document the API serves, and each example is a response the sandbox gave.
Reading an entry
Every path below is under the base URL, https://api.barrierops.com/api/v1, and every request but the document’s own sends Authorization: Bearer bo_live_…. An entry gives each method on the path with the scope it needs, its parameters, the body it takes, the fields it answers with and the statuses it can answer. Then one complete example, sent with a bo_test_ key: the command, and the response formatted for reading.
A field whose type is a schema’s name has that schema’s fields in a table of their own, under the first entry on this page that uses it. A field that holds an object of its own is followed by that object’s fields, named by their path, such as assignee.email. A field that holds one of several objects, such as chain in Report, has a table for each object, after the first table on this page that holds the field.
The same description, for a client generator, is the OpenAPI document at /docs/openapi.json and /docs/openapi.yaml. instance is the request id, which is different on every request; this page prints one fixed value in its place.
/openapi.json
This API’s OpenAPI 3.1 document, the one to generate a client from
GET /openapi.json
This document. The OpenAPI 3.1 description of this API, the one clients are generated from. It needs no key, and spends the budget of the address that asks.
Scope: none: this route needs no key
Answers with a JSON object.
| Field | Type | Description |
|---|
200— The OpenAPI 3.1 document429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support
The document itself is the example: the site publishes it at /docs/openapi.json.
/organizations
The organisations a key reaches: its own, or an agency’s clients
GET /organizations
The organisations this key reaches. An organisation key reaches its own organisation; an agency key, every client of the agency that it has not released — a suspended client is read only; a test key, the sandbox. By name.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
Answers with a page of Organization: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
id | uuid | The organisation |
name | string | Its name |
readOnly | boolean | True when this key reads the organisation and can change nothing in it, whatever its scopes: a test key reading the sandbox, or an agency key reaching a client whose link the agency has suspended |
200— The organisations, by name400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /organizations
bash: an example request to /organizations
curl https://api.barrierops.com/api/v1/organizations \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"id": "b7749447-87f3-5d35-beee-99e152f35574",
"name": "City of Riverbend",
"readOnly": true
},
{
"id": "ba8d4e08-bff9-5c3b-bb26-cf0b019d840b",
"name": "Riverbend Public Library",
"readOnly": true
},
{
"id": "76d68331-b591-5c16-95ea-41d27f95f95a",
"name": "Riverbend Water District",
"readOnly": true
}
],
"nextCursor": null,
"total": 3
}Lines are wrapped for printing. The original is one line.
/properties
The assets those organisations audit — list them, and add one
GET /properties
List assets. The live assets of every organisation the key reaches, newest first, or of one of them with orgId.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
orgId | query | uuid | No | Only this organisation’s assets. An organisation the key does not reach matches nothing. |
Answers with a page of Property: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
id | uuid | The asset |
orgId | uuid | The organisation the asset belongs to |
name | string | What the organisation calls it |
baseUrl | uri | The address scans start from |
kind | one of website, application or vendor_product | website, application, or vendor_product: a system a vendor runs |
verificationState | one of unverified, pending, verified, failed, lapsed or revoked | Whether the organisation has proved it controls the host. Nothing is scanned until this is verified. |
createdAt | date-time | When the asset was added |
deletedAt | date-time or null | When the asset was deleted, or null. A deleted asset is not listed, and is still read by its id so that the evidence about it stays nameable. |
200— The assets, newest first400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
POST /properties
Add an asset. Adds an asset to an organisation the key may change: its own, or with orgId, one client of its agency. The asset starts unverified — control of its host is proved in the app — and the edition’s asset limit applies.
Scope: admin
| Field | Type | Required | Description |
|---|---|---|---|
orgId | uuid | No | The organisation to add the asset to. Required when the key reaches more than one organisation, as an agency key does; otherwise the key’s own. |
name | string | Yes | What to call the asset |
baseUrl | uri | Yes | The address scans will start from |
kind | one of website, application or vendor_product | No | website when absent |
Answers with Property.
| Field | Type | Description |
|---|---|---|
id | uuid | The asset |
orgId | uuid | The organisation the asset belongs to |
name | string | What the organisation calls it |
baseUrl | uri | The address scans start from |
kind | one of website, application or vendor_product | website, application, or vendor_product: a system a vendor runs |
verificationState | one of unverified, pending, verified, failed, lapsed or revoked | Whether the organisation has proved it controls the host. Nothing is scanned until this is verified. |
createdAt | date-time | When the asset was added |
deletedAt | date-time or null | When the asset was deleted, or null. A deleted asset is not listed, and is still read by its id so that the evidence about it stays nameable. |
201— The asset, as created400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence403— The key cannot make this change: its scopes do not include the one the operation needs, it is a test key, or the agency has suspended its link to this organisation (typeagency-read-only)404— No such resource, or it is in an organisation this key does not reach409— The change conflicts with the current state:detailsays why415— The body has no Content-Type, or one other than application/json:detailsays so429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /properties
bash: an example request to /properties
curl https://api.barrierops.com/api/v1/properties \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"id": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"name": "riverbend.example",
"baseUrl": "https://www.riverbend.example",
"kind": "website",
"verificationState": "verified",
"createdAt": "2026-07-01T15:40:00.000Z",
"deletedAt": null
}
],
"nextCursor": null,
"total": 1
}Lines are wrapped for printing. The original is one line.
/properties/{id}
One asset — read it, and change its name or kind
GET /properties/{id}
Read an asset. One asset. A deleted asset is still read by its id, with deletedAt set.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
Answers with Property.
| Field | Type | Description |
|---|---|---|
id | uuid | The asset |
orgId | uuid | The organisation the asset belongs to |
name | string | What the organisation calls it |
baseUrl | uri | The address scans start from |
kind | one of website, application or vendor_product | website, application, or vendor_product: a system a vendor runs |
verificationState | one of unverified, pending, verified, failed, lapsed or revoked | Whether the organisation has proved it controls the host. Nothing is scanned until this is verified. |
createdAt | date-time | When the asset was added |
deletedAt | date-time or null | When the asset was deleted, or null. A deleted asset is not listed, and is still read by its id so that the evidence about it stays nameable. |
200— The asset400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
PATCH /properties/{id}
Change an asset. Changes an asset’s name or kind; a deleted asset cannot be changed. To monitor a different address, add an asset.
Scope: admin
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | A new name |
kind | one of website, application or vendor_product | No | A new kind |
Answers with Property.
| Field | Type | Description |
|---|---|---|
id | uuid | The asset |
orgId | uuid | The organisation the asset belongs to |
name | string | What the organisation calls it |
baseUrl | uri | The address scans start from |
kind | one of website, application or vendor_product | website, application, or vendor_product: a system a vendor runs |
verificationState | one of unverified, pending, verified, failed, lapsed or revoked | Whether the organisation has proved it controls the host. Nothing is scanned until this is verified. |
createdAt | date-time | When the asset was added |
deletedAt | date-time or null | When the asset was deleted, or null. A deleted asset is not listed, and is still read by its id so that the evidence about it stays nameable. |
200— The asset, as changed400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence403— The key cannot make this change: its scopes do not include the one the operation needs, it is a test key, or the agency has suspended its link to this organisation (typeagency-read-only)404— No such resource, or it is in an organisation this key does not reach409— The change conflicts with the current state:detailsays why415— The body has no Content-Type, or one other than application/json:detailsays so429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /properties/{id}
bash: an example request to /properties/{id}
curl https://api.barrierops.com/api/v1/properties/9ccf6432-ad06-5afe-8a30-52373a0346a4 \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"id": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"name": "riverbend.example",
"baseUrl": "https://www.riverbend.example",
"kind": "website",
"verificationState": "verified",
"createdAt": "2026-07-01T15:40:00.000Z",
"deletedAt": null
}Lines are wrapped for printing. The original is one line.
/properties/{id}/scans
An asset’s scan history, and the trigger that starts a scan
GET /properties/{id}/scans
List an asset’s scans. The asset’s scans, newest first, optionally of some statuses only.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
status | query | one of queued, crawling, scanning, complete or failed, or several as repeated keys | No | Only scans in these statuses |
Answers with a page of Scan: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
id | uuid | The scan |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset scanned |
status | one of queued, crawling, scanning, complete or failed | queued, crawling, scanning, complete or failed. The last two are final. |
createdAt | date-time | When the scan was asked for |
startedAt | date-time or null | When it started, or null |
finishedAt | date-time or null | When it finished, or null |
pagesDiscovered | integer | Pages the crawl found |
pagesScanned | integer | Pages a browser loaded and tested. A sample of each template is tested and its result attributed to the template’s other pages, so this is smaller than pagesDiscovered by design. |
pagesFailed | integer | Pages the scan tried and could not test |
truncated | boolean | The crawl stopped at the hard limit of 25,000 pages |
limited | boolean | The scan stopped at the edition’s or the pilot’s page limit. Pages it did not reach are not evidence that an issue is absent from them. |
engineVersionChanged | boolean | The scan ran on a different version of its engines from the scan before it, so a change between the two can come from the engines rather than the asset |
failureReason | one of cancelled, discovery_failed, discovery_incomplete, discovery_not_started, enqueue_failed, pilot_ended, not_verified, vendor_not_authorised, vendor_nothing_tested, vendor_visit_failed, trial_ended, subscription_ended, agency_suspended, agency_released or null | Why a failed scan ended without results, or null |
200— The scans, newest first400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
POST /properties/{id}/scans
Start a scan. Queues a scan of a verified asset whose scope has been confirmed. Refused with a 409 while the organisation already has as many unfinished scans as the edition lets a key start at once, and for the reasons the app refuses one — an unverified asset, an unconfirmed scope, a subscription that has ended.
Scope: write
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
Answers with Scan.
| Field | Type | Description |
|---|---|---|
id | uuid | The scan |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset scanned |
status | one of queued, crawling, scanning, complete or failed | queued, crawling, scanning, complete or failed. The last two are final. |
createdAt | date-time | When the scan was asked for |
startedAt | date-time or null | When it started, or null |
finishedAt | date-time or null | When it finished, or null |
pagesDiscovered | integer | Pages the crawl found |
pagesScanned | integer | Pages a browser loaded and tested. A sample of each template is tested and its result attributed to the template’s other pages, so this is smaller than pagesDiscovered by design. |
pagesFailed | integer | Pages the scan tried and could not test |
truncated | boolean | The crawl stopped at the hard limit of 25,000 pages |
limited | boolean | The scan stopped at the edition’s or the pilot’s page limit. Pages it did not reach are not evidence that an issue is absent from them. |
engineVersionChanged | boolean | The scan ran on a different version of its engines from the scan before it, so a change between the two can come from the engines rather than the asset |
failureReason | one of cancelled, discovery_failed, discovery_incomplete, discovery_not_started, enqueue_failed, pilot_ended, not_verified, vendor_not_authorised, vendor_nothing_tested, vendor_visit_failed, trial_ended, subscription_ended, agency_suspended, agency_released or null | Why a failed scan ended without results, or null |
202— The scan, queued400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence403— The key cannot make this change: its scopes do not include the one the operation needs, it is a test key, or the agency has suspended its link to this organisation (typeagency-read-only)404— No such resource, or it is in an organisation this key does not reach409— The change conflicts with the current state:detailsays why429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /properties/{id}/scans
bash: an example request to /properties/{id}/scans
curl "https://api.barrierops.com/api/v1/properties/9ccf6432-ad06-5afe-8a30-52373a0346a4/scans?limit=2" \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"id": "50763701-e469-5512-96d1-4181fa293810",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"status": "complete",
"createdAt": "2026-10-07T14:40:00.000Z",
"startedAt": "2026-10-07T14:41:00.000Z",
"finishedAt": "2026-10-07T15:14:00.000Z",
"pagesDiscovered": 4102,
"pagesScanned": 4102,
"pagesFailed": 0,
"truncated": false,
"limited": false,
"engineVersionChanged": false,
"failureReason": null
},
{
"id": "e68c305d-03b6-59e6-b477-01ecf259aaa4",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"status": "complete",
"createdAt": "2026-09-06T14:40:00.000Z",
"startedAt": "2026-09-06T14:41:00.000Z",
"finishedAt": "2026-09-06T15:14:00.000Z",
"pagesDiscovered": 4102,
"pagesScanned": 4102,
"pagesFailed": 0,
"truncated": false,
"limited": false,
"engineVersionChanged": false,
"failureReason": null
}
],
"nextCursor": "WyIyMDI2LTA5LTA2VDE0OjQwOjAwLjAwMDAwMFoiLCJlNjhjMzA1ZC0wM2I2LTU5ZTYtYjQ3Ny0wMWVjZjI1OWFhYTQiXQ",
"total": 4
}Lines are wrapped for printing. The original is one line.
/scans/{id}
One scan: where it stands and what it covered
GET /scans/{id}
Read a scan. One scan: where it stands, what it covered, and whether its engines changed.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The scan |
Answers with Scan.
| Field | Type | Description |
|---|---|---|
id | uuid | The scan |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset scanned |
status | one of queued, crawling, scanning, complete or failed | queued, crawling, scanning, complete or failed. The last two are final. |
createdAt | date-time | When the scan was asked for |
startedAt | date-time or null | When it started, or null |
finishedAt | date-time or null | When it finished, or null |
pagesDiscovered | integer | Pages the crawl found |
pagesScanned | integer | Pages a browser loaded and tested. A sample of each template is tested and its result attributed to the template’s other pages, so this is smaller than pagesDiscovered by design. |
pagesFailed | integer | Pages the scan tried and could not test |
truncated | boolean | The crawl stopped at the hard limit of 25,000 pages |
limited | boolean | The scan stopped at the edition’s or the pilot’s page limit. Pages it did not reach are not evidence that an issue is absent from them. |
engineVersionChanged | boolean | The scan ran on a different version of its engines from the scan before it, so a change between the two can come from the engines rather than the asset |
failureReason | one of cancelled, discovery_failed, discovery_incomplete, discovery_not_started, enqueue_failed, pilot_ended, not_verified, vendor_not_authorised, vendor_nothing_tested, vendor_visit_failed, trial_ended, subscription_ended, agency_suspended, agency_released or null | Why a failed scan ended without results, or null |
200— The scan400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /scans/{id}
bash: an example request to /scans/{id}
curl https://api.barrierops.com/api/v1/scans/50763701-e469-5512-96d1-4181fa293810 \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"id": "50763701-e469-5512-96d1-4181fa293810",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"status": "complete",
"createdAt": "2026-10-07T14:40:00.000Z",
"startedAt": "2026-10-07T14:41:00.000Z",
"finishedAt": "2026-10-07T15:14:00.000Z",
"pagesDiscovered": 4102,
"pagesScanned": 4102,
"pagesFailed": 0,
"truncated": false,
"limited": false,
"engineVersionChanged": false,
"failureReason": null
}Lines are wrapped for printing. The original is one line.
/properties/{id}/issues
An asset’s issues, filtered by impact, status, viewport and success criterion
GET /properties/{id}/issues
List an asset’s issues. The asset’s issues, the most recently found first. Filters take several values as repeated keys: ?impact=critical&status=open&status=assigned.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
status | query | one of open, assigned, in_progress, ready_to_verify, verified or deferred, or several as repeated keys | No | Only issues in these statuses |
impact | query | one of critical, serious, moderate or minor, or several as repeated keys | No | Only issues of these impacts |
viewport | query | one of desktop, mobile or tablet, or several as repeated keys | No | Only issues at these layouts |
wcagSC | query | string, or several as repeated keys | No | Only issues failing any of these success criteria, as 1.4.3 |
Answers with a page of Issue: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
id | uuid | The issue |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset it was found on |
title | string | What the barrier is |
wcagSC | array of string | The WCAG success criteria it fails |
impact | one of critical, serious, moderate or minor | critical, serious, moderate or minor |
status | one of open, assigned, in_progress, ready_to_verify, verified or deferred | open, assigned, in_progress, ready_to_verify (reported gone, not yet confirmed), verified (confirmed gone, on evidence) or deferred |
outcome | one of violation or incomplete | violation, or incomplete: the engine could not decide, and a person has to review it |
reviewState | one of pending, pass, fail or not_applicable | Where a person’s review of an incomplete finding stands. An incomplete issue whose review is pending cannot be marked ready to verify, verified or deferred. |
viewport | one of desktop, mobile or tablet | The layout it was found at. The same barrier at desktop and at mobile is two issues. |
assignee | object or null | Who owns it, or null |
assignee.email | string | The address the person signs in with |
assignee.name | string or null | Their name, when they gave one |
occurrenceCount | integer | Pages it is present on now, attributed pages included. Not a count that adds up across assets. |
firstSeenAt | date-time | When a scan first found it |
updatedAt | date-time | When it last changed |
200— The issues, the most recently found first400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /properties/{id}/issues
bash: an example request to /properties/{id}/issues
curl "https://api.barrierops.com/api/v1/properties/9ccf6432-ad06-5afe-8a30-52373a0346a4/issues?impact=critical&status=open&limit=2" \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"id": "274b937b-fb93-5927-9ba6-c81132eb1e9a",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"title": "Input buttons must have discernible text",
"wcagSC": [
"4.1.2"
],
"impact": "critical",
"status": "open",
"outcome": "violation",
"reviewState": "pending",
"viewport": "desktop",
"assignee": null,
"occurrenceCount": 30,
"firstSeenAt": "2026-10-07T15:14:00.000Z",
"updatedAt": "2026-10-07T15:14:00.000Z"
},
{
"id": "1ee558db-c390-5838-afd8-c25e3b05e69c",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"title": "ARIA attributes must conform to valid values",
"wcagSC": [
"4.1.2"
],
"impact": "critical",
"status": "open",
"outcome": "violation",
"reviewState": "pending",
"viewport": "desktop",
"assignee": null,
"occurrenceCount": 300,
"firstSeenAt": "2026-07-07T15:14:00.000Z",
"updatedAt": "2026-10-07T15:14:00.000Z"
}
],
"nextCursor": "eyJzb3J0IjoiZmlyc3RTZWVuQXQiLCJvcmRlciI6ImRlc2MiLCJkaXIiOiJhZnRlciIsInZhbHVlcyI6WyIyMDI2LTA3LTA3VDE1OjE0OjAwLjAwMDAwMFoiLCIxZWU1NThkYi1jMzkwLTU4MzgtYWZkOC1jMjVlM2IwNWU2OWMiXX0",
"total": 6
}Lines are wrapped for printing. The original is one line.
/issues/{id}
One issue, and the status and owner changes that move it
GET /issues/{id}
Read an issue. One issue in full, with its deferral record when it is deferred.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The issue |
Answers with IssueDetail.
| Field | Type | Description |
|---|---|---|
id | uuid | The issue |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset it was found on |
title | string | What the barrier is |
wcagSC | array of string | The WCAG success criteria it fails |
impact | one of critical, serious, moderate or minor | critical, serious, moderate or minor |
status | one of open, assigned, in_progress, ready_to_verify, verified or deferred | open, assigned, in_progress, ready_to_verify (reported gone, not yet confirmed), verified (confirmed gone, on evidence) or deferred |
outcome | one of violation or incomplete | violation, or incomplete: the engine could not decide, and a person has to review it |
reviewState | one of pending, pass, fail or not_applicable | Where a person’s review of an incomplete finding stands. An incomplete issue whose review is pending cannot be marked ready to verify, verified or deferred. |
viewport | one of desktop, mobile or tablet | The layout it was found at. The same barrier at desktop and at mobile is two issues. |
assignee | object or null | Who owns it, or null |
assignee.email | string | The address the person signs in with |
assignee.name | string or null | Their name, when they gave one |
occurrenceCount | integer | Pages it is present on now, attributed pages included. Not a count that adds up across assets. |
firstSeenAt | date-time | When a scan first found it |
updatedAt | date-time | When it last changed |
plainDescription | string or null | What is wrong, in plain language, when someone has written it; else null |
regressedAt | date-time or null | When it most recently came back after being verified, or null |
deferral | object or null | The deferral record of a deferred issue; null otherwise |
deferral.reason | string | Why it was deferred |
deferral.owner | object or null | Who owns the decision |
deferral.owner.email | string | The address the person signs in with |
deferral.owner.name | string or null | Their name, when they gave one |
deferral.reviewAt | date | When the decision is to be reviewed |
200— The issue400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
PATCH /issues/{id}
Change an issue’s status or owner. Moves an issue to a status, assigns it, or both, by the rules the app applies: an incomplete finding needs a person’s review before it can be marked ready to verify, verified or deferred; ready to verify needs a page it is present on and a scan that recorded its engine. The evidence ledger records, naming the key, an assignment to a person, a move to ready to verify, verified or deferred, and a move back from one of those three; any other change adds no event.
Scope: write
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The issue |
| Field | Type | Required | Description |
|---|---|---|---|
status | one of open, assigned, in_progress, ready_to_verify, verified or deferred | No | The status to move the issue to |
assigneeEmail | email or null | No | The person to assign it to, by address; null unassigns |
deferredReason | string | No | Why it is deferred, at least 20 characters. Required to defer. |
deferredOwnerEmail | No | Who owns the deferral, by address. Required to defer. | |
deferredReviewAt | date | No | When to review the deferral, YYYY-MM-DD. Required to defer. |
verificationMethod | one of automated or manual | No | How it was verified, automated or manual. Required to verify. |
Answers with IssueDetail.
| Field | Type | Description |
|---|---|---|
id | uuid | The issue |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset it was found on |
title | string | What the barrier is |
wcagSC | array of string | The WCAG success criteria it fails |
impact | one of critical, serious, moderate or minor | critical, serious, moderate or minor |
status | one of open, assigned, in_progress, ready_to_verify, verified or deferred | open, assigned, in_progress, ready_to_verify (reported gone, not yet confirmed), verified (confirmed gone, on evidence) or deferred |
outcome | one of violation or incomplete | violation, or incomplete: the engine could not decide, and a person has to review it |
reviewState | one of pending, pass, fail or not_applicable | Where a person’s review of an incomplete finding stands. An incomplete issue whose review is pending cannot be marked ready to verify, verified or deferred. |
viewport | one of desktop, mobile or tablet | The layout it was found at. The same barrier at desktop and at mobile is two issues. |
assignee | object or null | Who owns it, or null |
assignee.email | string | The address the person signs in with |
assignee.name | string or null | Their name, when they gave one |
occurrenceCount | integer | Pages it is present on now, attributed pages included. Not a count that adds up across assets. |
firstSeenAt | date-time | When a scan first found it |
updatedAt | date-time | When it last changed |
plainDescription | string or null | What is wrong, in plain language, when someone has written it; else null |
regressedAt | date-time or null | When it most recently came back after being verified, or null |
deferral | object or null | The deferral record of a deferred issue; null otherwise |
deferral.reason | string | Why it was deferred |
deferral.owner | object or null | Who owns the decision |
deferral.owner.email | string | The address the person signs in with |
deferral.owner.name | string or null | Their name, when they gave one |
deferral.reviewAt | date | When the decision is to be reviewed |
200— The issue, as changed400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence403— The key cannot make this change: its scopes do not include the one the operation needs, it is a test key, or the agency has suspended its link to this organisation (typeagency-read-only)404— No such resource, or it is in an organisation this key does not reach409— The change conflicts with the current state:detailsays why415— The body has no Content-Type, or one other than application/json:detailsays so429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /issues/{id}
bash: an example request to /issues/{id}
curl https://api.barrierops.com/api/v1/issues/274b937b-fb93-5927-9ba6-c81132eb1e9a \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"id": "274b937b-fb93-5927-9ba6-c81132eb1e9a",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"title": "Input buttons must have discernible text",
"wcagSC": [
"4.1.2"
],
"impact": "critical",
"status": "open",
"outcome": "violation",
"reviewState": "pending",
"viewport": "desktop",
"assignee": null,
"occurrenceCount": 30,
"firstSeenAt": "2026-10-07T15:14:00.000Z",
"updatedAt": "2026-10-07T15:14:00.000Z",
"plainDescription": null,
"regressedAt": null,
"deferral": null
}Lines are wrapped for printing. The original is one line.
/issues/{id}/occurrences
The pages an issue appears on — one row per page
GET /issues/{id}/occurrences
List the pages an issue is on. One row per page, the most recently seen first: the pages a browser loaded and, unless attributed=false, the pages that carry the issue by template.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The issue |
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
state | query | one of present or fixed | No | Only present, or only fixed |
attributed | query | string | No | Include the pages that carry the issue by template. true when absent; false lists only the pages a browser loaded. |
Answers with a page of Occurrence: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
issueId | uuid | The issue |
pageUrl | uri | The page |
pageTitle | string or null | The page’s title, when it has one |
state | one of present or fixed | present, or fixed: the latest scan of the page no longer found it. Fixed is about this page alone; the issue’s status says what was confirmed. |
evidenceSource | one of captured or attributed | captured: a browser loaded this page and found it. attributed: the page carries it by template, from a sampled page of the same template. |
sampledFromPageUrl | uri or null | For an attributed row, the sampled page its evidence comes from; else null |
domPath | string or null | Where on the page the element is, as a path through the document |
lastSeenAt | date-time | When a scan last saw it on this page |
200— The pages, the most recently seen first400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /issues/{id}/occurrences
bash: an example request to /issues/{id}/occurrences
curl "https://api.barrierops.com/api/v1/issues/274b937b-fb93-5927-9ba6-c81132eb1e9a/occurrences?limit=2" \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"issueId": "274b937b-fb93-5927-9ba6-c81132eb1e9a",
"pageUrl": "https://www.riverbend.example/parks/wagon-wheel-park/trails",
"pageTitle": "Wagon Wheel Park: trails | City of Riverbend",
"state": "present",
"evidenceSource": "attributed",
"sampledFromPageUrl": "https://www.riverbend.example/parks/founders-park",
"domPath": null,
"lastSeenAt": "2026-10-07T15:14:00.000Z"
},
{
"issueId": "274b937b-fb93-5927-9ba6-c81132eb1e9a",
"pageUrl": "https://www.riverbend.example/parks/red-fox-park/parking",
"pageTitle": "Red Fox Park: parking | City of Riverbend",
"state": "present",
"evidenceSource": "attributed",
"sampledFromPageUrl": "https://www.riverbend.example/parks/founders-park",
"domPath": null,
"lastSeenAt": "2026-10-07T15:14:00.000Z"
}
],
"nextCursor": "eyJzb3J0IjoibGFzdFNlZW4iLCJvcmRlciI6ImRlc2MiLCJkaXIiOiJhZnRlciIsInZhbHVlcyI6WyIyMDI2LTEwLTA3VDE1OjE0OjAwLjAwMDAwMFoiLCIwMTBlYzVhOS0wZDdjLTU4YzUtYTViNy1lNTNmNTgwMTFkNjYiXX0",
"total": 245
}Lines are wrapped for printing. The original is one line.
/properties/{id}/documents
The documents an asset links to, and what checking them found
GET /properties/{id}/documents
List an asset’s documents. The documents the asset links to and what checking each found: failed first, then the largest within each state, including the files that could not be checked.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
status | query | one of discovered, triaged, validated or failed, or several as repeated keys | No | Only documents in these statuses |
Answers with a page of Document: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
id | uuid | The document |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset it was found on |
url | uri | Where the document is |
title | string or null | The title the file carries, exactly as it carries it, or null |
format | one of pdf, word, excel, powerpoint or other | pdf, word, excel, powerpoint or other |
mimeType | string or null | The media type it was served with |
byteSize | integer or null | Its size in bytes |
pageCount | integer or null | Its pages, when known |
status | one of discovered, triaged, validated or failed | discovered, triaged, validated (checked, with no failed rule) or failed. Validated is not a statement that the document is accessible. |
checkState | one of failed, unreadable, too_large, format_unsupported, triaged, discovered or validated | What checking it found, including the files that could not be checked: unreadable, too large, or a format that is recorded and not validated |
linkedFromUrl | uri or null | The page it was linked from, when it was found by following a link |
lastSeenAt | date-time | When a crawl last found it |
200— The documents400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /properties/{id}/documents
bash: an example request to /properties/{id}/documents
curl "https://api.barrierops.com/api/v1/properties/9ccf6432-ad06-5afe-8a30-52373a0346a4/documents?status=failed&limit=2" \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"id": "983ffca1-f9ae-5cfb-b556-de64e93a2c17",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"url": "https://www.riverbend.example/meetings/city-council/2026-09-18/packet.pdf",
"title": "City Council meeting packet, September 18, 2026",
"format": "pdf",
"mimeType": "application/pdf",
"byteSize": 69710,
"pageCount": 1,
"status": "failed",
"checkState": "failed",
"linkedFromUrl": "https://www.riverbend.example/meetings/city-council/2026-09-18",
"lastSeenAt": "2026-10-07T15:14:20.000Z"
},
{
"id": "df7306d2-9af6-52b5-af5a-98a66f5a4472",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"url": "https://www.riverbend.example/meetings/city-council/2026-09-09/packet.pdf",
"title": "City Council meeting packet, September 9, 2026",
"format": "pdf",
"mimeType": "application/pdf",
"byteSize": 69317,
"pageCount": 1,
"status": "failed",
"checkState": "failed",
"linkedFromUrl": "https://www.riverbend.example/meetings/city-council/2026-09-09",
"lastSeenAt": "2026-10-07T15:14:20.000Z"
}
],
"nextCursor": "eyJzb3J0Ijoic3RhdHVzIiwib3JkZXIiOiJhc2MiLCJkaXIiOiJhZnRlciIsInZhbHVlcyI6WzEsMCwiNjkzMTciLCJkZjczMDZkMi05YWY2LTUyYjUtYWY1YS05OGE2NmY1YTQ0NzIiXX0",
"total": 16
}Lines are wrapped for printing. The original is one line.
/documents/{id}
One document
GET /documents/{id}
Read a document. One document and what checking it found.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The document |
Answers with Document.
| Field | Type | Description |
|---|---|---|
id | uuid | The document |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset it was found on |
url | uri | Where the document is |
title | string or null | The title the file carries, exactly as it carries it, or null |
format | one of pdf, word, excel, powerpoint or other | pdf, word, excel, powerpoint or other |
mimeType | string or null | The media type it was served with |
byteSize | integer or null | Its size in bytes |
pageCount | integer or null | Its pages, when known |
status | one of discovered, triaged, validated or failed | discovered, triaged, validated (checked, with no failed rule) or failed. Validated is not a statement that the document is accessible. |
checkState | one of failed, unreadable, too_large, format_unsupported, triaged, discovered or validated | What checking it found, including the files that could not be checked: unreadable, too large, or a format that is recorded and not validated |
linkedFromUrl | uri or null | The page it was linked from, when it was found by following a link |
lastSeenAt | date-time | When a crawl last found it |
200— The document400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /documents/{id}
bash: an example request to /documents/{id}
curl https://api.barrierops.com/api/v1/documents/983ffca1-f9ae-5cfb-b556-de64e93a2c17 \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"id": "983ffca1-f9ae-5cfb-b556-de64e93a2c17",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"url": "https://www.riverbend.example/meetings/city-council/2026-09-18/packet.pdf",
"title": "City Council meeting packet, September 18, 2026",
"format": "pdf",
"mimeType": "application/pdf",
"byteSize": 69710,
"pageCount": 1,
"status": "failed",
"checkState": "failed",
"linkedFromUrl": "https://www.riverbend.example/meetings/city-council/2026-09-18",
"lastSeenAt": "2026-10-07T15:14:20.000Z"
}Lines are wrapped for printing. The original is one line.
/properties/{id}/vendors
The vendor products linked to an asset, and the evidence on file for each
GET /properties/{id}/vendors
List the vendor products linked to an asset. The vendor products linked to the asset, with the state of the evidence for each: the oldest VPAT first, and the products with none before them.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
Answers with a page of VendorProduct: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
id | uuid | The vendor product |
orgId | uuid | The organisation that bought it |
propertyId | uuid or null | The asset it is linked to, or null |
vendorId | uuid | The vendor |
vendorName | string | The vendor’s name |
name | string | The product’s name |
vpatVersion | string or null | The version of the VPAT on file, as written on it, or null |
vpatDate | date or null | The date of the VPAT on file, or null |
evidenceStatus | one of none, requested, received, stale or accepted | none, requested, received, stale or accepted. Stale when the VPAT is more than 24 months old, whatever was recorded before. |
contractClause | boolean | An accessibility clause is on file in the contract |
slaDays | integer or null | Days the contract gives the vendor to remediate, or null |
nextReviewAt | date or null | When the evidence is next reviewed |
notes | string or null | The organisation’s own notes |
createdAt | date-time | When the product was recorded |
updatedAt | date-time | When it last changed |
200— The vendor products400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
POST /properties/{id}/vendors
Record a vendor product for an asset. Records a product bought from a vendor of the asset’s organisation, linked to the asset. Its evidence starts at none.
Scope: write
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
| Field | Type | Required | Description |
|---|---|---|---|
vendorId | uuid | Yes | The vendor, in the asset’s organisation, the product is bought from |
name | string | Yes | The product’s name |
contractClause | boolean | No | An accessibility clause is on file in the contract |
slaDays | integer or null | No | Days the contract gives the vendor to remediate |
nextReviewAt | date or null | No | When the evidence is next to be reviewed |
notes | string or null | No | Notes |
Answers with VendorProduct.
| Field | Type | Description |
|---|---|---|
id | uuid | The vendor product |
orgId | uuid | The organisation that bought it |
propertyId | uuid or null | The asset it is linked to, or null |
vendorId | uuid | The vendor |
vendorName | string | The vendor’s name |
name | string | The product’s name |
vpatVersion | string or null | The version of the VPAT on file, as written on it, or null |
vpatDate | date or null | The date of the VPAT on file, or null |
evidenceStatus | one of none, requested, received, stale or accepted | none, requested, received, stale or accepted. Stale when the VPAT is more than 24 months old, whatever was recorded before. |
contractClause | boolean | An accessibility clause is on file in the contract |
slaDays | integer or null | Days the contract gives the vendor to remediate, or null |
nextReviewAt | date or null | When the evidence is next reviewed |
notes | string or null | The organisation’s own notes |
createdAt | date-time | When the product was recorded |
updatedAt | date-time | When it last changed |
201— The vendor product, as recorded400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence403— The key cannot make this change: its scopes do not include the one the operation needs, it is a test key, or the agency has suspended its link to this organisation (typeagency-read-only)404— No such resource, or it is in an organisation this key does not reach409— The change conflicts with the current state:detailsays why415— The body has no Content-Type, or one other than application/json:detailsays so429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /properties/{id}/vendors
bash: an example request to /properties/{id}/vendors
curl https://api.barrierops.com/api/v1/properties/9ccf6432-ad06-5afe-8a30-52373a0346a4/vendors \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"id": "1215dd9d-fc89-5035-aac0-ee10e5dbf851",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"vendorId": "7b1c4f1e-4baf-5118-99c4-0fc13b49199f",
"vendorName": "Northfork Civic Software (fictional)",
"name": "Agenda Center",
"vpatVersion": "2.5Rev",
"vpatDate": "2025-09-12",
"evidenceStatus": "received",
"contractClause": false,
"slaDays": null,
"nextReviewAt": null,
"notes": null,
"createdAt": "2026-07-15T19:00:00.000Z",
"updatedAt": "2026-07-15T19:00:00.000Z"
}
],
"nextCursor": null,
"total": 1
}Lines are wrapped for printing. The original is one line.
/vendors/{id}
One vendor product, and the changes a script may make to it
GET /vendors/{id}
Read a vendor product. One vendor product and the state of the evidence for it.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The vendor product |
Answers with VendorProduct.
| Field | Type | Description |
|---|---|---|
id | uuid | The vendor product |
orgId | uuid | The organisation that bought it |
propertyId | uuid or null | The asset it is linked to, or null |
vendorId | uuid | The vendor |
vendorName | string | The vendor’s name |
name | string | The product’s name |
vpatVersion | string or null | The version of the VPAT on file, as written on it, or null |
vpatDate | date or null | The date of the VPAT on file, or null |
evidenceStatus | one of none, requested, received, stale or accepted | none, requested, received, stale or accepted. Stale when the VPAT is more than 24 months old, whatever was recorded before. |
contractClause | boolean | An accessibility clause is on file in the contract |
slaDays | integer or null | Days the contract gives the vendor to remediate, or null |
nextReviewAt | date or null | When the evidence is next reviewed |
notes | string or null | The organisation’s own notes |
createdAt | date-time | When the product was recorded |
updatedAt | date-time | When it last changed |
200— The vendor product400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
PATCH /vendors/{id}
Change a vendor product. Changes a vendor product’s name, its contract terms or its notes.
Scope: write
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The vendor product |
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | A new name |
contractClause | boolean | No | An accessibility clause is on file in the contract |
slaDays | integer or null | No | Days the contract gives the vendor to remediate |
notes | string or null | No | Notes |
Answers with VendorProduct.
| Field | Type | Description |
|---|---|---|
id | uuid | The vendor product |
orgId | uuid | The organisation that bought it |
propertyId | uuid or null | The asset it is linked to, or null |
vendorId | uuid | The vendor |
vendorName | string | The vendor’s name |
name | string | The product’s name |
vpatVersion | string or null | The version of the VPAT on file, as written on it, or null |
vpatDate | date or null | The date of the VPAT on file, or null |
evidenceStatus | one of none, requested, received, stale or accepted | none, requested, received, stale or accepted. Stale when the VPAT is more than 24 months old, whatever was recorded before. |
contractClause | boolean | An accessibility clause is on file in the contract |
slaDays | integer or null | Days the contract gives the vendor to remediate, or null |
nextReviewAt | date or null | When the evidence is next reviewed |
notes | string or null | The organisation’s own notes |
createdAt | date-time | When the product was recorded |
updatedAt | date-time | When it last changed |
200— The vendor product, as changed400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence403— The key cannot make this change: its scopes do not include the one the operation needs, it is a test key, or the agency has suspended its link to this organisation (typeagency-read-only)404— No such resource, or it is in an organisation this key does not reach409— The change conflicts with the current state:detailsays why415— The body has no Content-Type, or one other than application/json:detailsays so429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /vendors/{id}
bash: an example request to /vendors/{id}
curl https://api.barrierops.com/api/v1/vendors/1215dd9d-fc89-5035-aac0-ee10e5dbf851 \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"id": "1215dd9d-fc89-5035-aac0-ee10e5dbf851",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"vendorId": "7b1c4f1e-4baf-5118-99c4-0fc13b49199f",
"vendorName": "Northfork Civic Software (fictional)",
"name": "Agenda Center",
"vpatVersion": "2.5Rev",
"vpatDate": "2025-09-12",
"evidenceStatus": "received",
"contractClause": false,
"slaDays": null,
"nextReviewAt": null,
"notes": null,
"createdAt": "2026-07-15T19:00:00.000Z",
"updatedAt": "2026-07-15T19:00:00.000Z"
}Lines are wrapped for printing. The original is one line.
/properties/{id}/evidence
The evidence ledger, oldest first, in the order the hash chain is walked
GET /properties/{id}/evidence
Read an asset’s evidence ledger. The asset’s ledger, oldest first — the order the hash chain is walked in — optionally of one issue, some events or a range of time.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
issueId | query | uuid | No | Only events about this issue |
event | query | one of discovered, fixed, regressed, assigned, verified, accepted or reopened, or several as repeated keys | No | Only these events |
from | query | date-time | No | Only events recorded at or after this |
to | query | date-time | No | Only events recorded at or before this |
Answers with a page of EvidenceEvent: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
id | string | The event, a decimal string: it outgrows a JavaScript number |
propertyId | uuid | The asset whose chain it is in |
issueId | uuid or null | The issue it is about, or null |
scanId | uuid or null | The scan that recorded it, or null |
event | one of discovered, fixed, regressed, assigned, verified, accepted or reopened | discovered, fixed (on one page), regressed, assigned, verified, accepted (a deferral decided) or reopened |
pageUrl | string or null | The page it is about, or null |
actor | EvidenceActor | Who recorded the event: a person in the app, an API key by its name, or automated — a scan, or the reconciliation that follows one |
createdAt | date-time | When it was recorded |
eventHash | string | The event’s hash in the chain, 64 lowercase hexadecimal characters: the value the evidence package’s sidecar carries for it |
| Field | Type | Description |
|---|---|---|
type | person | |
email | string | Their address |
name | string or null | Their name, when they gave one |
| Field | Type | Description |
|---|---|---|
type | api_key | |
name | string | The key’s name |
| Field | Type | Description |
|---|---|---|
type | automated |
200— The ledger, oldest first400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /properties/{id}/evidence
bash: an example request to /properties/{id}/evidence
curl "https://api.barrierops.com/api/v1/properties/9ccf6432-ad06-5afe-8a30-52373a0346a4/evidence?limit=2" \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"id": "1",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"issueId": "3bb0d247-96bf-57e6-bd39-e1f832023670",
"scanId": "1f991ae8-c29d-5a0c-ac20-e2ecdc086aa8",
"event": "discovered",
"pageUrl": "https://www.riverbend.example/",
"actor": {
"type": "automated"
},
"createdAt": "2026-07-07T15:14:00.000Z",
"eventHash": "d248208409812b5a1bf5a865ff8ad742e8c9a645af46234fd28854bbaab673ad"
},
{
"id": "2",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"issueId": "a9e10f40-7697-5ad9-aa24-c67c9e432d05",
"scanId": "1f991ae8-c29d-5a0c-ac20-e2ecdc086aa8",
"event": "discovered",
"pageUrl": "https://www.riverbend.example/",
"actor": {
"type": "automated"
},
"createdAt": "2026-07-07T15:14:00.000Z",
"eventHash": "dc6a00d18a2c4cc4017cd57538fb17655f343ae4e3967e0900d32c096ac33446"
}
],
"nextCursor": "eyJzb3J0IjoiY3JlYXRlZCIsImRpciI6ImFmdGVyIiwiYXQiOiIyMDI2LTA3LTA3VDE1OjE0OjAwLjAwMDAwMVoiLCJpZCI6IjIifQ",
"total": 5710
}Lines are wrapped for printing. The original is one line.
/properties/{id}/evidence/verify
Recompute the hash chain and return what the walk found
POST /properties/{id}/evidence/verify
Verify an asset’s evidence chain. Recomputes every hash of the asset’s ledger and says what the walk found: intact, broken at a named event, or unverifiable with the reason. There is no fourth outcome, and a walk that read nothing is unverifiable. The check is recorded in the audit log, naming the key.
Scope: write
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The asset |
Answers with EvidenceVerification.
| Field | Type | Description |
|---|---|---|
propertyId | uuid | |
status | intact | |
eventCount | integer | |
from | date-time | |
to | date-time |
| Field | Type | Description |
|---|---|---|
propertyId | uuid | |
status | broken | |
firstBrokenEventId | string | |
firstBrokenEventPosition | integer | |
firstBrokenEventAt | date-time |
| Field | Type | Description |
|---|---|---|
propertyId | uuid | |
status | unverifiable | |
reason | string |
200— What the walk found400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence403— The key cannot make this change: its scopes do not include the one the operation needs, it is a test key, or the agency has suspended its link to this organisation (typeagency-read-only)404— No such resource, or it is in an organisation this key does not reach409— The change conflicts with the current state:detailsays why429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /properties/{id}/evidence/verify
bash: an example request to /properties/{id}/evidence/verify
curl -X POST https://api.barrierops.com/api/v1/properties/9ccf6432-ad06-5afe-8a30-52373a0346a4/evidence/verify \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 403
{
"type": "https://barrierops.app/problems/forbidden",
"title": "This key does not permit this action",
"status": 403,
"detail": "Test keys read the fixture organisation and change nothing.",
"instance": "00000000-0000-4000-8000-000000000000"
}Lines are wrapped for printing. The original is one line.
A test key is refused this, as every change. With a live key that has the write scope, the answer is one of three outcomes — the ones the fields above list — and a client that treats anything but intact as a failure is reading it correctly:
| status | What it means |
|---|---|
intact | Every event recomputed, with eventCount and the range the walk covered, from and to |
broken | The walk found an event that does not verify: its id, its position in the chain and when it was recorded |
unverifiable | The chain could not be read, with the reason — and that is not a pass |
There are exactly three outcomes and none of them is “ok”.
/reports
Generate a report, and list the reports there are
GET /reports
List reports. Reports across every organisation the key reaches, newest first; or of one organisation, one asset or one kind.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
cursor | query | string | No | The nextCursor of the page before this one; absent for the first page. Opaque and not signed: do not parse or build one. A cursor the API cannot read is answered with the first page; a readable one is read as the position it names, so pass back exactly the value you were given. |
limit | query | integer from 1 to 200 | No | How many items to return, 1 to 200; 50 when absent. A larger value is refused with a 400 rather than trimmed. |
orgId | query | uuid | No | Only this organisation’s reports |
propertyId | query | uuid | No | Only this asset’s reports |
kind | query | one of executive_summary, technical_detail or evidence_package | No | Only reports of this kind |
Answers with a page of Report: items, nextCursor and total.
| Field | Type | Description |
|---|---|---|
id | uuid | The report |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset it reports on |
kind | one of executive_summary, technical_detail or evidence_package | executive_summary, technical_detail or evidence_package |
status | one of queued, rendering, ready or failed | queued, rendering, ready or failed. Only a ready report has a PDF. |
language | string | The report’s language, as a BCP 47 tag |
scanId | uuid or null | The scan it is built from, or null |
compareScanId | uuid or null | The scan an executive summary compares with, or null |
periodFrom | date-time or null | An evidence package’s first instant, or null |
periodTo | date-time or null | The instant after an evidence package’s last, or null: the range is half-open |
chain | object, or anything or null | An evidence package’s chain verification, frozen when it was made; else null |
requestedBy | ReportRequester | Who asked for the report: a person, a schedule, or an API key by its name |
requestedAt | date-time | When it was asked for |
completedAt | date-time or null | When it became ready, or null |
failedReason | string or null | Why it failed, or null |
linkExpiresAt | date-time or null | Until when the report can be downloaded, 90 days from when it became ready unless the link is renewed in the app; null until it is ready |
downloadUrl | uri or null | A link to the PDF, signed for 15 minutes from this response. Null unless the report is ready and its link has not expired, and always null in a demo or sandbox organisation, from which no file is exported. |
downloadUrlExpiresAt | date-time or null | When downloadUrl stops working, or null |
| Field | Type | Description |
|---|---|---|
status | intact | |
eventCount | integer | |
from | date-time | |
to | date-time |
| Field | Type | Description |
|---|---|---|
status | broken | |
firstBrokenEventId | string |
| Field | Type | Description |
|---|---|---|
status | unverifiable | |
reason | string |
| Field | Type | Description |
|---|---|---|
type | person | |
email | string | Their address |
name | string or null | Their name, when they gave one |
| Field | Type | Description |
|---|---|---|
type | scheduled |
| Field | Type | Description |
|---|---|---|
type | api_key | |
name | string | The key’s name |
200— The reports, newest first400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
POST /reports
Generate a report. Queues a report on an asset: an executive summary or a technical report built from a completed scan, or an evidence package of a range of days. It is queued, not finished: read it until its status is ready, or subscribe to report.ready.
Scope: write
| Field | Type | Required | Description |
|---|---|---|---|
kind | executive_summary | Yes | |
propertyId | uuid | Yes | The asset to report on |
scanId | uuid | Yes | A completed scan of the asset |
compareScanId | uuid or null | No | A second completed scan to compare with, or null |
language | string | No | en-US when absent |
| Field | Type | Required | Description |
|---|---|---|---|
kind | technical_detail | Yes | |
propertyId | uuid | Yes | The asset to report on |
scanId | uuid | Yes | A completed scan of the asset |
status | array of one of open, assigned, in_progress, ready_to_verify, verified or deferred | No | Only issues in these statuses; every status when empty |
impact | array of one of critical, serious, moderate or minor | No | Only issues of these impacts; every impact when empty |
language | string | No | en-US when absent |
| Field | Type | Required | Description |
|---|---|---|---|
kind | evidence_package | Yes | |
propertyId | uuid | Yes | The asset to report on |
from | date | Yes | The first day, YYYY-MM-DD |
to | date | Yes | The last day, YYYY-MM-DD, included |
language | string | No | en-US when absent |
Answers with Report.
| Field | Type | Description |
|---|---|---|
id | uuid | The report |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset it reports on |
kind | one of executive_summary, technical_detail or evidence_package | executive_summary, technical_detail or evidence_package |
status | one of queued, rendering, ready or failed | queued, rendering, ready or failed. Only a ready report has a PDF. |
language | string | The report’s language, as a BCP 47 tag |
scanId | uuid or null | The scan it is built from, or null |
compareScanId | uuid or null | The scan an executive summary compares with, or null |
periodFrom | date-time or null | An evidence package’s first instant, or null |
periodTo | date-time or null | The instant after an evidence package’s last, or null: the range is half-open |
chain | object, or anything or null | An evidence package’s chain verification, frozen when it was made; else null |
requestedBy | ReportRequester | Who asked for the report: a person, a schedule, or an API key by its name |
requestedAt | date-time | When it was asked for |
completedAt | date-time or null | When it became ready, or null |
failedReason | string or null | Why it failed, or null |
linkExpiresAt | date-time or null | Until when the report can be downloaded, 90 days from when it became ready unless the link is renewed in the app; null until it is ready |
downloadUrl | uri or null | A link to the PDF, signed for 15 minutes from this response. Null unless the report is ready and its link has not expired, and always null in a demo or sandbox organisation, from which no file is exported. |
downloadUrlExpiresAt | date-time or null | When downloadUrl stops working, or null |
201— The report, queued400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence403— The key cannot make this change: its scopes do not include the one the operation needs, it is a test key, or the agency has suspended its link to this organisation (typeagency-read-only)404— No such resource, or it is in an organisation this key does not reach409— The change conflicts with the current state:detailsays why415— The body has no Content-Type, or one other than application/json:detailsays so429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /reports
bash: an example request to /reports
curl https://api.barrierops.com/api/v1/reports \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"items": [
{
"id": "5d07da36-9e7c-5cf8-83c6-2ac4cb46b4df",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"kind": "evidence_package",
"status": "ready",
"language": "en",
"scanId": null,
"compareScanId": null,
"periodFrom": "2026-07-01T00:00:00.000Z",
"periodTo": "2026-10-09T00:00:00.000Z",
"chain": {
"status": "intact",
"eventCount": 5710,
"from": "2026-07-07T15:14:00.000Z",
"to": "2026-10-07T15:14:00.002Z"
},
"requestedBy": {
"type": "person",
"email": "ruth.delacroix@riverbend.example",
"name": "Ruth Delacroix"
},
"requestedAt": "2026-10-08T15:30:00.000Z",
"completedAt": "2026-10-08T15:30:00.000Z",
"failedReason": null,
"linkExpiresAt": "2027-01-06T15:30:00.000Z",
"downloadUrl": null,
"downloadUrlExpiresAt": null
}
],
"nextCursor": null,
"total": 1
}Lines are wrapped for printing. The original is one line.
/reports/{id}
One report: its status, and its link once it is ready
GET /reports/{id}
Read a report. One report: its status, and once it is ready and while its link policy holds, a link to its PDF signed for fifteen minutes.
Scope: read
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | uuid | Yes | The report |
Answers with Report.
| Field | Type | Description |
|---|---|---|
id | uuid | The report |
orgId | uuid | The organisation of the asset |
propertyId | uuid | The asset it reports on |
kind | one of executive_summary, technical_detail or evidence_package | executive_summary, technical_detail or evidence_package |
status | one of queued, rendering, ready or failed | queued, rendering, ready or failed. Only a ready report has a PDF. |
language | string | The report’s language, as a BCP 47 tag |
scanId | uuid or null | The scan it is built from, or null |
compareScanId | uuid or null | The scan an executive summary compares with, or null |
periodFrom | date-time or null | An evidence package’s first instant, or null |
periodTo | date-time or null | The instant after an evidence package’s last, or null: the range is half-open |
chain | object, or anything or null | An evidence package’s chain verification, frozen when it was made; else null |
requestedBy | ReportRequester | Who asked for the report: a person, a schedule, or an API key by its name |
requestedAt | date-time | When it was asked for |
completedAt | date-time or null | When it became ready, or null |
failedReason | string or null | Why it failed, or null |
linkExpiresAt | date-time or null | Until when the report can be downloaded, 90 days from when it became ready unless the link is renewed in the app; null until it is ready |
downloadUrl | uri or null | A link to the PDF, signed for 15 minutes from this response. Null unless the report is ready and its link has not expired, and always null in a demo or sandbox organisation, from which no file is exported. |
downloadUrlExpiresAt | date-time or null | When downloadUrl stops working, or null |
200— The report400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /reports/{id}
bash: an example request to /reports/{id}
curl https://api.barrierops.com/api/v1/reports/5d07da36-9e7c-5cf8-83c6-2ac4cb46b4df \
-H "Authorization: Bearer $BARRIEROPS_API_KEY"Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"id": "5d07da36-9e7c-5cf8-83c6-2ac4cb46b4df",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"propertyId": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"kind": "evidence_package",
"status": "ready",
"language": "en",
"scanId": null,
"compareScanId": null,
"periodFrom": "2026-07-01T00:00:00.000Z",
"periodTo": "2026-10-09T00:00:00.000Z",
"chain": {
"status": "intact",
"eventCount": 5710,
"from": "2026-07-07T15:14:00.000Z",
"to": "2026-10-07T15:14:00.002Z"
},
"requestedBy": {
"type": "person",
"email": "ruth.delacroix@riverbend.example",
"name": "Ruth Delacroix"
},
"requestedAt": "2026-10-08T15:30:00.000Z",
"completedAt": "2026-10-08T15:30:00.000Z",
"failedReason": null,
"linkExpiresAt": "2027-01-06T15:30:00.000Z",
"downloadUrl": null,
"downloadUrlExpiresAt": null
}Lines are wrapped for printing. The original is one line.
/cms/content
Post a page’s address from a CMS and receive its known issues, split into content-authored and template-level
POST /cms/content
Look up a page’s known issues for a CMS. For a CMS: the issues known for one address, split into those the content’s author can fix in the editor — with the content editor’s guidance where it is published — and those that belong to the template or the theme. A read: nothing is stored, and the content identity is returned as sent.
Scope: read
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The page’s canonical address |
nodeId | string | No | The CMS’s own id for the content. Returned as sent, and never stored. |
contentType | string | No | The CMS’s content type. Returned as sent, and never stored. |
revisionId | string | No | The CMS’s revision id. Returned as sent, and never stored. |
Answers with ContentLookup.
| Field | Type | Description |
|---|---|---|
url | string | The address as it was looked up |
nodeId | string or null | The nodeId sent, or null |
contentType | string or null | The contentType sent, or null |
revisionId | string or null | The revisionId sent, or null |
property | object or null | The asset the address belongs to, or null when no asset has its host |
property.id | uuid | The asset |
property.orgId | uuid | Its organisation |
property.name | string | Its name |
page | object or null | The page, or null when no crawl has found the address |
page.url | string | The page, as the crawl recorded it |
page.lastScannedAt | date-time or null | When a browser last loaded the page in a scan, or null: a page covered by its template’s sample carries the template’s issues and was not loaded itself |
contentAuthored | array of ContentAuthoredIssue | Issues the page’s author can fix in the editor |
templateLevel | array of ContentIssue | Every other issue present on the page: its template’s or its theme’s, fixed in the code rather than the content |
| Field | Type | Description |
|---|---|---|
issueId | uuid | The issue; read it at /issues/{id} |
title | string | What the barrier is |
impact | one of critical, serious, moderate or minor | critical, serious, moderate or minor |
status | one of open, assigned, in_progress, ready_to_verify, verified or deferred | Its status, one of the six |
wcagSC | array of string | The WCAG success criteria it fails |
guidance | object or null | The content-editor guidance for the issue’s rule when it is published; else null |
guidance.summary | string or null | The fix, in a sentence |
guidance.whyItMatters | string or null | Who it stops, and how |
guidance.howToFix | string or null | The steps, in Markdown |
| Field | Type | Description |
|---|---|---|
issueId | uuid | The issue; read it at /issues/{id} |
title | string | What the barrier is |
impact | one of critical, serious, moderate or minor | critical, serious, moderate or minor |
status | one of open, assigned, in_progress, ready_to_verify, verified or deferred | Its status, one of the six |
wcagSC | array of string | The WCAG success criteria it fails |
200— The address’s issues, split400— The query, the path or the body is not valid:detailnames each field and what is wrong with it, or says that the body is not JSON401— No key, or the key was not accepted: unknown, revoked, expired, or a secret a rotation has retired. Every reason gets the same sentence404— No such resource, or it is in an organisation this key does not reach415— The body has no Content-Type, or one other than application/json:detailsays so429— The budget is spent. Retry after the number of seconds in Retry-After500— The request could not be completed. Quoteinstanceto support503— A test key’s sandbox is still being prepared. Retry after the number of seconds in Retry-After
Example: /cms/content
bash: an example request to /cms/content
curl -X POST https://api.barrierops.com/api/v1/cms/content \
-H "Authorization: Bearer $BARRIEROPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.riverbend.example/services/west/report-an-abandoned-vehicle"}'Lines are wrapped for printing. The original is one line.
json: its complete response, a 200
{
"url": "https://www.riverbend.example/services/west/report-an-abandoned-vehicle",
"nodeId": null,
"contentType": null,
"revisionId": null,
"property": {
"id": "9ccf6432-ad06-5afe-8a30-52373a0346a4",
"orgId": "b7749447-87f3-5d35-beee-99e152f35574",
"name": "riverbend.example"
},
"page": {
"url": "https://www.riverbend.example/services/west/report-an-abandoned-vehicle",
"lastScannedAt": "2026-10-07T15:11:01.000Z"
},
"contentAuthored": [],
"templateLevel": [
{
"issueId": "5b418aa0-7d5e-5633-8e7a-9e942ed8c81e",
"title": "Form elements must have labels",
"impact": "critical",
"status": "deferred",
"wcagSC": [
"4.1.2"
]
},
{
"issueId": "b3ae5c0a-6991-5d03-953d-8e212bb40f57",
"title": "ARIA input fields must have an accessible name",
"impact": "serious",
"status": "open",
"wcagSC": [
"4.1.2"
]
}
]
}Lines are wrapped for printing. The original is one line.
The sandbox has no content-authored issue, so for a bo_test_ key contentAuthored is always empty, as it is here, and templateLevel holds every issue on the page. The fields a live key gets in contentAuthored are in the ContentAuthoredIssue table above.
Why this is one page
There is no search box. Every endpoint, parameter and field is on one reference page, so your browser’s own search reaches all of it.
Every endpoint is in this document, none of it is behind a disclosure, and the whole of it prints. That is what replaces a search field — and it is also what makes the page usable at 400% zoom, where a search result you cannot see the context of is worse than a long page you can scroll.
The changelog records every change to this surface, including the day a deprecation is decided rather than the day it ships. The messages the API sends rather than answers are on Webhooks.