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/organizationsLines 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.rotatedandapikey.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:assignedwhen it gives an issue an owner;fixedwhen it moves an issue toready_to_verify, one for each page the issue is on;verifiedandacceptedwhen it moves an issue toverifiedordeferred; andreopenedwhen it moves an issue back fromready_to_verify,verifiedordeferred. Any other status change adds no event, and neither does unassigning. A report a key asks for names the key inrequestedBy. - 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.
| Scope | What it allows | Endpoints |
|---|---|---|
read | Read assets, scans, issues, documents, vendors, evidence and reports, and change nothing |
|
write | Start scans, change an issue’s status and assignee, record and edit vendor products, request reports and check the evidence chain |
|
admin | Add assets and change their settings |
|
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
- 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.
- Deploy the new secret everywhere the key is used.
- Confirm the previous secret’s last-used time has stopped moving. The key’s row shows the two separately while both work.
- 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
- Revoke it first and ask questions second. Revocation takes effect on the next request, and it is recorded in the audit log.
- Then grep your repositories for bo_live_ and bo_test_, to find anywhere else the key, or another one, was committed.
- 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.