Skip to main content
digital accessibility compliance for state and local government
Scan a site

Authentication

Last updated 28 October 2026 · API version v1

An API key in an Authorization header. Not OAuth — our consumers are server-to-server scripts, not third-party apps acting for users.

Sending the key

Every request but the document’s own carries the key in one header:

Authorization: Bearer bo_live_…

Never in a query string, never in a cookie, never in a custom header. The scheme is read in any case, so bearer is accepted too. A request without an accepted key is refused with a 401 and one sentence, whatever the reason — no key, a malformed one, an unknown one, a revoked one, or a secret a rotation has retired — so a guessed key learns nothing:

bash: a request without a key

curl https://api.barrierops.com/api/v1/organizations

Lines are wrapped for printing. The original is one line.

json: the complete response, a 401

{
  "type": "https://barrierops.app/problems/unauthenticated",
  "title": "Authentication is required",
  "status": 401,
  "detail": "The API key was not accepted. Send it as Authorization: Bearer, or create a new one under Settings, API keys.",
  "instance": "00000000-0000-4000-8000-000000000000"
}

Lines are wrapped for printing. The original is one line.

instance is the request id, which is different on every request; this page prints one fixed value in its place.

What a key is

Two prefixes
bo_live_ for a key that reads and changes your organisation, bo_test_ for a key that reads the sandbox. The bo_live_ and bo_test_ prefixes exist so a leaked key is greppable. Grep your own repositories for both.
Shown once
Hashed at rest with Argon2id. Shown in full exactly once, at creation. A key is created under Settings, API keys with Create key, by an admin or an owner. The key is shown in full exactly once. After that you see the prefix and the last four. We cannot recover it and we will not pretend to.
One organisation, or one agency
A key belongs to the organisation it was created in. An agency’s key reaches every client of the agency that it has not released, which is what makes a portfolio-wide report one script rather than one per client. Only the Agency owner creates or changes one; any admin or owner can revoke it.
Its own limits and its own record
Each key has its own rate limit, its own last-used time and its own scopes. Creating, rotating and revoking a key are recorded in your organisation’s audit log, as apikey.created, apikey.rotated and apikey.revoked.
Where its changes are recorded
The audit log records, under the key’s name, the assets a key adds or changes (property.created, property.updated), the scans it starts (scan.triggered) and the evidence checks it runs (evidence.chain_verified). The evidence ledger records, naming the key, the issue changes it makes that are evidence, as these events: assigned when it gives an issue an owner; fixed when it moves an issue to ready_to_verify, one for each page the issue is on; verified and accepted when it moves an issue to verified or deferred; and reopened when it moves an issue back from ready_to_verify, verified or deferred. Any other status change adds no event, and neither does unassigning. A report a key asks for names the key in requestedBy.
Test keys
A bo_test_ key has the read scope and no other. It reads the sandbox and is refused every change with a 403, whatever its organisation’s own keys may do.

Scopes

Three, and they follow the application’s roles. Every key has read; write and admin are added when the key is created or edited.

The three scopes a key can carry, what each allows and the endpoints it opens. 3 rows.
ScopeWhat it allowsEndpoints
readRead assets, scans, issues, documents, vendors, evidence and reports, and change nothing
  • Every GET — 15 endpoints
  • POST /cms/content
writeStart scans, change an issue’s status and assignee, record and edit vendor products, request reports and check the evidence chain
  • POST /properties/{id}/scans
  • PATCH /issues/{id}
  • POST /properties/{id}/vendors
  • PATCH /vendors/{id}
  • POST /properties/{id}/evidence/verify
  • POST /reports
adminAdd assets and change their settings
  • POST /properties
  • PATCH /properties/{id}

A key without the scope an operation needs is refused before the request is read, with the scope it lacks in the sentence:

bash: add an asset with a key that has read alone

curl -X POST https://api.barrierops.com/api/v1/properties \
  -H "Authorization: Bearer $BARRIEROPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Riverbend Parks","baseUrl":"https://parks.riverbend.example"}'

Lines are wrapped for printing. The original is one line.

json: the complete response, a 403

{
  "type": "https://barrierops.app/problems/forbidden",
  "title": "This key does not permit this action",
  "status": 403,
  "detail": "This key’s scopes do not include admin.",
  "instance": "00000000-0000-4000-8000-000000000000"
}

Lines are wrapped for printing. The original is one line.

A reporting script wants read and nothing else. It is the scope that cannot change anything in your programme, which is worth more than the convenience of one key that does everything.

Rotating a key without downtime

  1. Rotate the key on its row in the application, choosing how long the previous secret keeps working: 1 hour, 24 hours, 7 days or 30 days — 7 days unless you choose otherwise. The new secret is shown once, like the first.
  2. Deploy the new secret everywhere the key is used.
  3. Confirm the previous secret’s last-used time has stopped moving. The key’s row shows the two separately while both work.
  4. End the rotation, which retires the previous secret at once — or let the overlap run out, which retires it then.

Both secrets are accepted for the whole overlap, so nothing has to be switched at a particular moment. A key that is already rotating cannot be rotated again until its rotation ends. If you would rather keep two separate keys, create a second key instead: each has its own name, budget and scopes.

If a key leaks

  1. Revoke it first and ask questions second. Revocation takes effect on the next request, and it is recorded in the audit log.
  2. Then grep your repositories for bo_live_ and bo_test_, to find anywhere else the key, or another one, was committed.
  3. Then tell us at security@barrierops.com if it reached a public repository, so we can look for use of it.

The order is the point. Revoking first costs you a redeploy; investigating first costs you the time the key stays live. Our vulnerability disclosure policy covers what happens after you tell us, including the windows we commit to.

https://barrierops.com/docs/authentication