# SecBoost API v1 agent guide

## Start here

Resource requests use the HTTPS base URL `/api/v1`. This guide covers both human
integrations and agents. Use the [OpenAPI contract](../openapi.json) for exact
paths, fields, operation IDs and permission metadata; use
[authentication](authentication.md) for credential selection and lifetime limits.

| Work you need to do | Credential type | Target |
| --- | --- | --- |
| Create organisations; maintain shared External Providers | Administration | No organisation/project binding; organisation access still checked |
| Prepare organisation/system context, create/configure/populate projects, maintain shared contacts/registers | Organisation | One organisation |
| Work on controls, evidence, assessments, project registers, Docsuite and tasks | Project | One project |

1. Sign in and open **API tokens** in the account menu. Create a credential of
   the required type with the smallest permission selection and its dependencies.
   Leave read-only enabled for reads; disable it explicitly for mutations. Store
   the secret when shown: it cannot be retrieved later.
2. Make a permitted discovery/read request. For example, a project credential
   with `view-projects` can request:

   ```http
   GET /api/v1/projects
   Authorization: Bearer <token>
   Accept: application/json
   ```

3. Use returned resource IDs. For writes, inspect the operation's allowlist,
   permission requirements, execution mode and side effects. Retain the ETag
   from the relevant resource read and send it unchanged in `If-Match` wherever
   the operation requires it. Use `Idempotency-Key` only for operations that
   document creation receipts; it does not replace `If-Match`.
4. Read back or poll the documented status endpoint after asynchronous work.
   A successful start response does not guarantee completion. After a lost
   response, inspect current state before retrying. Follow [safe recovery](#safe-recovery).

Approval and provider publication require explicit permission selection and normal
workflow prerequisites. Users, memberships, roles, teams and credentials are
managed through the authenticated UI; the resource API cannot administer them.
Organisation and project work need separate credentials. Creating an organisation
with an Administration credential does not issue the next credential for you.

## Permission and execution contract

The API has 113 resource operations. Credentials select canonical application
permissions through the `abilities` transport field and independently select
read-only execution. Authority combines selected/live permissions, mode, explicit
type/target and unchanged domain workflow. Shared permission names do not extend
type boundaries; no retired-grant aliases or role/global-staff bypass exist.
API-started continuations remain tied to their original actor/credential, including
context/scope applicability work. Reissue does not transfer an old job to a new key.
If context population fails, read both project state and `GET .../population`.
For an unarchived Initialising project with failed context population, refresh the
setup ETag and POST the same empty population request using current management
authority. This starts a new run (`run_id`), keeping existing controls,
recommendations and documents and filling missing entries from current context.
Existing recommendation snapshots are retained; recovery does not reconcile them
against changed scope. Normal context/scope recalculation remains the maintenance path.
Restore previously archived projects through the UI first. Failed import/copy runs and legacy failed rows without known context provenance
are excluded: recovery cannot safely infer their origin. An active/completed run cannot be restarted.
Old jobs and credentials are never adopted. On an uncertain response, read back
before retrying; a changed run invalidates the old ETag (412), including within the
same second. Completed progress is temporary, so also read project lifecycle state.

## Administration organisation provisioning

1. Issue an Administration key through the credential-confirmed token page. Select
   `manage-org-hierarchy` for discovery/read-back and creation. Leave read-only
   execution enabled for reads; disable it explicitly if creation is required.
2. Discover `/organisations` (50 identities per page). GET
   `/organisations/{organisation}` to read back a currently accessible identity
   and its ETag. Use returned IDs; selected UI context is never authority.
3. If needed, POST `/organisations` with a unique `Idempotency-Key`, name and
   timezone. Follow the OpenAPI field allowlist. Normal bootstrap and licence
   limits apply; never submit members, owners, teams, roles or permissions.
4. Reuse a creation key with identical payload only within the 24-hour receipt
   window. After expiry, discover/read back before attempting creation again.
5. Issue an organisation management key through the authenticated UI, bound to
   the resulting organisation, to continue context/system/project setup,
   population, shared contacts and registers. Administration cannot perform these
   operations or issue the next key for you.
6. Issue a project operations key through the UI, bound to the prepared project,
   for controls, evidence, assessments, library, Docsuite and tasks. Use ETags for
   mutations; after 412 reread and reconcile the intended edit.

Published-provider selection is an organisation setup operation. User management
and credential issuance are UI-only.
Do not fabricate unknown facts or approve claims automatically.

SecBoost API v1 lets an authorised human user's project-bound bearer token perform
the documented operational work in one project. A distinct organisation-bound
token supports organisation-management setup. Neither credential replaces the
user's current SecBoost permissions or access.

## External Providers

Provider administration uses an Administration key with `manage-org-hierarchy`.
Read-only execution permits authorised reads; identity, upload and import changes
require mutation-enabled execution. Select `publish-provider-assurance-packages`
explicitly for release configuration, assertion review and publication. Select
`view-restricted-provider-artefacts` or `upload-restricted-provider-artefacts`
independently when that source/action requires it. Each also requires selected/live
hierarchy authority. Upload authority does not imply restricted content reads.

Issue through the token page with normal session/CSRF/recent-confirmation gates.
The HTTP field remains `abilities`, containing canonical application permission
values; `read_only` is independent and defaults to true. A preparation credential
can select `["manage-org-hierarchy"]` with `read_only: false`. Selecting publication
or editing permissions never enables mutations. A bearer key cannot issue another
key. Provider UI/API actor authority is aligned without changing seeded role grants.

Use the returned key as a bearer credential for `/api/v1` resource requests.
Browser smoke clients should omit session cookies for bearer requests so Sanctum
uses the bearer credential rather than the signed-in web session.

1. Discover `/external-providers` and `/external-providers/options`. Providers are
   installation-owned and have no organisation parent. Use returned IDs and
   catalogue selections; no source-package or seeder identity is required.
2. Create/edit provider identities and assurance releases. Use `Idempotency-Key`
   for creates and current strong `If-Match` ETags for updates/deletion. Release
   configuration also requires selected and live publication permission.
3. Create artefact links or upload files through `/external-providers/{provider}/artefacts`.
   Links are references only and are never fetched automatically. Bulk uploads use
   `/assurance-releases/{package}/artefacts` under the provider, with its release
   ETag, 1–25 files and a maximum of 50 MiB per file. Files are quarantined/scanned.
4. Read artefact detail to inspect upload/import status and results. Ready stored
   files have a protected `file_url`. Restricted artefacts are omitted from lists
   without restricted read authority. Upload-only restricted creation returns a
   minimal receipt, and does not grant subsequent reads.
5. Start `/artefacts/{artefact}/import` or `/use-as-ccm` with the artefact ETag.
   Both return 202; poll artefact detail rather than blindly repeating an action.
   Queued work rechecks the initiating token, account and live permissions before
   durable import. Supported parser profiles and normal ISM validation remain.
6. Discover paginated `/artefacts/{artefact}/assertions`. Each row includes its
   `etag` for individual `/confirm` or `/reject`. For a corrected mapping, query
   `/external-providers/options?ism_version_id={sourceVersion}&control_id={controlIdentifier}`
   for authorised catalogue reference IDs; follow its pagination for broader discovery. `/assertions/confirm-exact` uses
   the artefact ETag and normal exact-match/warning gates. Review records the actor;
   imported claims are never automatically treated as approved.
7. Publish `/assurance-releases/{package}/publish` with its current ETag and the
   explicit publication permission. All normal ready-file, checksum and review gates
   remain. Published releases cannot be edited; prepare a replacement release.

Creates replay their original result/ETag for 24 hours, bound to the credential,
operation, parent and canonical payload. A conflicting receipt returns 409. After
expiry, discover/read before retrying. On 412, reread and reconcile rather than
reusing a stale ETag. Deletion checks active uploads/imports, restricted material,
project references and retained provenance; an unreferenced release can be deleted
only through its normal domain deletion contract. Existing organisation/project
keys cannot administer providers; organisation keys still select published
provider releases as part of project scope.

## Organisation project setup

An organisation-management token is distinct from a project token. It can only
operate below its one bound organisation and only with the selected management
permissions. First [prepare organisation context](#prepare-the-organisation) and
[system context](#system-setup-and-shared-data) through the API or UI. Then use:

1. `GET /api/v1/organisations/{organisation}` and `/systems` to discover the prepared target.
2. `POST /api/v1/organisations/{organisation}/systems/{system}/projects` to create a Setup project.
3. Read or PATCH the nested project, `/scope`, and `/engagement` endpoints as appropriate.
4. `POST .../population`, then poll `GET .../population` until processing completes.

Every existing-project setup mutation requires the strong `ETag` returned by a
project, scope, engagement, artefact, or population read in `If-Match`. A
missing ETag returns `428`; a stale ETag returns `412`. Diagram uploads use
multipart form data and remain subject to the normal asynchronous scanning
pipeline. Narrative content can be supplied directly or generated from the
current project scope. These artefacts require `manage-projects`.

Project creation is intentionally not a bulk operation. Supply a unique
`Idempotency-Key` for each intended create; a matching retry returns the
original response for 24 hours, while a reused key with a changed payload is
rejected. After that retention period, reconcile by listing the known system
before retrying. Scope/provider
changes use normal validation and live permissions. Do not supply ownership,
workflow, account, membership, or provider-publication fields: they are rejected
or unavailable. A project key cannot call these setup endpoints.

## System setup and shared data

Use an organisation token with the selected system/shared permissions. Existing
organisation keys do not receive new permissions: issue a new named key through
the UI when additional permissions are needed. A project-management permission
alone cannot create systems or edit canonical context. Shared authoring also
requires the corresponding contact/register permissions.

1. `POST /organisations/{organisation}/systems` with `manage-org-hierarchy`, a unique
   `Idempotency-Key` and, for example, `{"name":"Example Service","short_code":"EXAMPLE"}`.
   Persist the returned system ID. Short codes are normalised to uppercase and
   unique per organisation. Licence limits apply under the organisation lock.
2. GET `/systems/{system}` for identity and its ETag; PATCH the same URL with
   `manage-org-hierarchy` and `If-Match` to edit only documented identity/contact fields.
3. GET `/systems/{system}/context` with `manage-org-hierarchy`. The returned
   definition provides field IDs, option values and version. PATCH with
   `manage-org-hierarchy` and that context ETag, using
   `{"data":{"hosting_model":"hybrid"},"edit_reason":"Customer confirmed hosting"}`
   only if `hybrid` appears in the current definition. Unknown facts stay unknown.
   Data merges field by field; null clears an individual answer. Omission keeps
   existing answers and review dates; `data: null` is rejected. Context changes
   use the normal provenance and applicability recalculation across sibling projects.
4. Continue the existing project setup/scope/artefact/population flow using this
   API-created system. Organisation context still must exist before project creation.
5. Read/create shared contacts at `/systems/{system}/contacts`, then read one
   contact and PATCH `/contacts/{contact}` with its ETag. Use the shared contact
   permissions; fields follow the normal contact schema. Deleted contacts are absent.
6. Discover `/systems/{system}/registers`, select a returned system-owned manual
   definition, and GET/POST `/registers/{definition}/rows`. Read a single row and
   PATCH `/rows/{row}` with its ETag and `{"data":{...}}`. Required columns, enums
   and logical keys come from the manifest. Contact references require
   selected and live `view-contacts` and active IDs in this exact system.
   Control/document fields are redacted and cannot be selected by organisation keys.

All paths above begin `/api/v1/organisations/{organisation}`. Contact and row lists
are paginated with `page[number]` and `page[size]` (default 50, maximum 100).
All three create families require receipts: keep the same token, target, payload
and key when recovering from a lost response. Matching retries replay for 24 hours;
changed target/payload returns 409. After expiry, reconcile list results before
creating again. PATCH uses `application/merge-patch+json`, requires the ETag from
that resource's read, returns 428 if absent and 412 if stale. Read again and reconcile
rather than retrying an overwrite. Contact/register receipts recheck current access;
register source references are re-filtered on replay.

Shared mutations affect other projects using that system; register changes refresh
their evidence drift. Writes to archived systems are rejected. No delete/archive,
restore, review-stamp, generated-register authoring, organisation editing/creation
or supplier-assurance publication API is added here. Architecture diagrams and
narratives remain project setup artefacts; there is no system-owned upload resource.

## Operational project work

Use the bound project ID in every URL below. You do not need to change the user's
UI-selected project. `GET /projects/{project}/inputs` and `/engagement` require
`view-projects`. Full `/context` and `/scope` reads require `view-projects` plus
`manage-projects`. These reads do not authorise setup changes or provider selection
through a project credential. All mutations require mutation-enabled execution.

Discover `/contacts` and `/registers` before composing a register row. Definitions
return manifest column IDs, types, required columns, enum IDs and a `writable`
hint. List rows at `/registers/{definition}/rows`, then GET `/{row}` for its ETag.
Only project-owned manual definitions/rows can be authored; same-system shared
rows are read-only, organisation-owned rows are not project context, and generated
registers are normal Docsuite outputs rather than manually authored rows.

Use GET/POST
`/projects/{project}/registers/{definition}/reviews` for manual definitions. GET
requires selected/live `view-registers` and returns only this project's history,
newest reviewed time then ID, with the usual bounded pagination. POST requires
selected/live `view-registers` and `manage-registers`, mutation-enabled execution
and an editable project. Send only optional nullable `notes` (maximum 5000
characters); the server attributes the review to the actor and current time.
Omitted notes and null are equivalent. Generated definitions return 404.

A review is a user assertion in this project, including when the definition is
system/organisation-owned. It does not snapshot or approve contents, certify the
shared owner's review, change register rows or mark readiness. Record only an
actual review authorised by the user. POST requires `Idempotency-Key`: matching
token/action/target/notes replay the original 201 for 24 hours, after fresh access,
permission, mode and lifecycle checks. Changed target/notes return 409. No If-Match
is required for append-only recording. After receipt expiry, read history before
creating another review. Evidence metadata editing, removal and cancellation are
described below.

For evidence association metadata, GET/PATCH
`/projects/{project}/controls/{control}/evidence/{evidence}`. GET requires selected/
live `view-controls` and returns stored metadata plus an item ETag. PATCH also
requires `edit-control-implementation`, mutation-enabled execution, an editable
project and an Editing control. Register sources additionally require selected/live
`view-registers`; source parent boundaries and library view authority still apply.

Use `application/merge-patch+json` and the current **item** ETag in `If-Match`.
The control/content ETag is a different precondition. Missing/stale item ETags return
428/412; reread and reconcile before retrying. Send at least one of `title`,
`description`, `evidence_type`, `classification`, `source_system`,
`external_reference`, `external_access_instructions`, `collected_on`, `review_due`.
Omission preserves values; null clears. Responses return stored association values,
which can differ from inherited source labels/dates in control detail.

Status/verification/currency claims, UI aliases, source kind/IDs, actor/provenance,
paths and processing fields are rejected. Editing metadata does not replace or
fetch source content, verify a connection, change implementation claims or rewrite
assessment snapshots. Metadata can be edited on pending/rejected uploads and
unavailable repository sources. Repository credentials remain UI-only;
removal/cancellation and uploaded-file replacement are delivered below.

POST multipart `/projects/{project}/controls/{control}/evidence/{evidence}/replacement`
with `file` and optional nullable `description`. Use the current metadata item ETag
in `If-Match`; omission/null preserves the description. Normal file rules and a
20 MiB limit apply. It requires selected/live `view-controls` and
`edit-control-implementation`, mutation-enabled execution, an editable project and
an Editing control. Non-file sources and processing uploads return 409.

The 202 response supplies a new pending item ID. Replacement immediately retires
the old association, carries its metadata/user assertions and preserves old files
referenced by shared evidence or assessment snapshots. Carried assertions do not
verify new bytes. Scan/content validation and live original-credential checks must
succeed before release. Poll control detail for the new item's file status;
metadata GET returns its next item ETag. Reissue cannot adopt the old operation.
There is no creation receipt: the retired target returns 404 on retry. After an
uncertain response, read control evidence before attempting another replacement.

```http
POST /api/v1/projects/{project}/registers/{definition}/rows
Authorization: Bearer <token>
Accept: application/json
Content-Type: application/json
Idempotency-Key: example-risk-001

{"data":{"id":"RISK-001","description":"Fictional example; replace with verified facts"}}
```

Use only actual manifest columns, not the fictional column assumptions above.
Creation and task comments require `Idempotency-Key` (missing/invalid keys return
`400`): matching canonical payloads
replay their original `201` response for 24 hours, scoped to token, operation and
target. A changed payload/target with the same key returns `409`. After expiry,
reconcile the row/comment list before issuing a new create. Existing evidence
creation retains its separately documented retry behaviour.

PATCH rows using `application/merge-patch+json` and the latest row ETag in
`If-Match`. The body is `{"data":{"column_id":"new value"}}`. Omitted columns
remain unchanged; `null` clears nullable columns. Required columns, declared
types/enums, logical-key uniqueness and scoped source references are validated.
Selecting a contact requires `view-contacts`; controls require `view-controls`;
document references require `view-docsuite`, each with the live source permission.
Use contact ULIDs, same-project control ULIDs or reference codes, and document
template keys from `/documents`. `redacted_fields` identifies source fields not
available to this credential; do not attempt to reconstruct or overwrite them.

Read `/library` to discover active definition ULIDs and working copies. GET
`/library/{definition}` returns an ETag, including when no copy exists yet; reads
never create empty records. PATCH with that ETag and only `content_markdown`,
`owner_user_id`, `classification`, `date_live` or `review_due`. Omitted fields
are preserved. Use paginated `/document-owners` for active organisation-member IDs/names.
Dates use YYYY-MM-DD, review due cannot precede live date, and classification uses
NC/OS/P/S/TS. Normal working-copy audit, evidence drift and unversioned-change
handling apply; version stamping remains part of normal document generation.
Markdown attributes may use only `class` and `id`; arbitrary styles and URL-bearing
attributes return 422. PDF generation embeds only the document project's registered diagram and organisation logo and does not
retrieve arbitrary network or local-file assets.

Read `/documents` for the project's normal template catalogue and output rows.
`document_key` identifies a template/reference; `{document}` in detail, download
and generation URLs is the returned output row ULID. Read detail for dependencies.
When a catalogue entry has no output row yet, POST `/documents/generation` with
`document_key` from the catalogue plus the options below; this initialises the
normal slot only as part of an authorised generation action. After a lost response,
reconcile `/documents` before retrying. No prior visit to the UI is required.
POST `/{document}/generation` with optional `generation_options`:

```json
{"generation_options":{"draft_watermark":false},"approval_confirmed":true}
```

Omit `draft_watermark` or set true for draft. Final output needs explicit approval;
SecBoost records the current actor and server timestamp. Do not supply approval
snapshots, storage paths, workflow states or Essential Eight maturity overrides.
Supported options are `draft_watermark`, `show_implementation_notes` and
`show_assessor_notes`. Generation returns `202` and a `Location` to poll; it uses
the normal project lease and status/dependency rules. After a lost response, poll
before resubmitting; another active generation is not started. Download existing
draft/final output at `/{document}/download`, accepting its PDF, JSON or XLSX MIME
type. Status exposes safe errors, not backend paths or exception details.
Revoked/expired credentials or lost live authority stop queued work before durable
operations; UI-started jobs are unaffected.

Read `/tasks`, then `/{task}` for its ETag. POST `/{task}/claim` or `/release`
with an empty JSON object and `If-Match`; these actions affect only the current
actor, subject to the normal team/assignee/manage-tasks policy. Read or POST
`/{task}/comments`; creation accepts only `{"comment":"Known fact"}` (max 5000
characters) and requires an idempotency key. Task/comment lists are bounded pages.
Manager overrides/visibility additionally require selected/live `manage-tasks`
and its `view-tasks` dependency. Read-only execution forbids task actions even
though their ordinary application permission is `view-tasks`. Related-control IDs
are omitted without selected/live `view-controls`. Tasks with a related control
outside the bound project are unavailable. Arbitrary
task creation, status/team/assignment changes and comment edits/deletes are excluded.

Contacts, document owners, register rows, tasks and comments use `page[number]` (default 1) and
`page[size]` (default 50, max 100). Definition/template discovery is a finite
catalogue. Mutations returning `428` need `If-Match`; after `412`, GET again and
reconcile instead of blindly retrying. Permissions and writable hints never replace
live authority. Setup/shared writes, deletion, bulk generation, cancellation and
credential/permission administration remain excluded; see the design inventory.

## Control operating loop

1. For Viewer-safe identity and setup discovery, call
   `GET /api/v1/projects/{project}/inputs` with `view-projects`. This
   endpoint never provides controls, evidence, assessment, scope or context
   data, and cannot be used to edit setup.
2. Call `GET /api/v1/projects` with a token that has `view-projects`.
3. Use the returned project ID in every control URL.
4. Find a control with `GET /api/v1/projects/{project}/controls`.
5. Retrieve control detail and retain its `ETag` header.
6. Inspect `allowed_actions` and `allowed_transitions`. They are planning hints,
   not lasting authority.
7. Send the retained ETag in `If-Match` when updating or transitioning.
8. Use the returned detail representation and new ETag for the next action.

The API exposes all configured workflow transitions. Implementation actions use
`edit-control-implementation`, and assessment actions use
`edit-control-assessment`. Approval or rejection requires the specific
implementation or assessment approval permission and the issuing user's current
approval permission. Inspect `allowed_transitions` before each action.

Every assessment workflow action also requires `view-control-assessment` and
the issuing user's current assessment-view permission. This prevents an agent
from submitting, completing or reopening assessment work it cannot inspect.

## Authentication and least privilege

Send the token only in an HTTPS request header:

```http
Authorization: Bearer <token>
Accept: application/json
```

Use only supported application permissions required by the integration and their
readable-context dependencies. The credential defaults to read-only execution;
explicitly disable it for any mutation. Approval/publication are separate selections
and are excluded from presets/select-all. Mode and current permissions are checked
again for locked mutation/replay and queued-work checkpoints. See OpenAPI
`x-required-permission`, alternatives, conditional permissions and `x-execution-mode`.

Use only the permissions required by the integration. Reading assessment fields
requires both `view-control-assessment` and the user's assessment-view
permission. Without both authorities, assessment properties are absent rather
than `null`.

See [authentication.md](authentication.md) for the permission list and token
lifecycle.

## Read, update, transition

```http
GET /api/v1/projects/01PROJECT/controls/01CONTROL
Authorization: Bearer <token>
Accept: application/json
```

After retaining the returned ETag, update implementation data with JSON Merge
Patch. Omitted properties remain unchanged and an explicit `null` clears a
nullable property. Applicability may be changed between `y` (applicable) and
`n` (not applicable). Never submit `ec`: classification exclusions are derived
when the project controls are created and cannot be set or overridden.

SSP and CCM `*_proposed_*` properties are assessed-party draft claims and use
the implementation endpoint. Only an assessment-authorised caller may read or
write `*_assessed_*` conclusions. Never derive a conclusion from a proposal or
copy an upstream provider obligation into downstream consumer guidance.

Control detail includes read-only `provider_guidance` derived from the project's
confirmed provider scope. Treat its upstream provider statements and
assessed-system customer obligations as context for the existing implementation
response. Advisories explain source-version differences, review dates and a
missing implementation summary; they are not separate decisions to submit.

`evidence_items` contains the same current evidence inventory used by the
assessment screen, including external references and linked-source provenance.
Treat it as live context: evidence or linked-source changes alter the ETag, so
retrieve the control again before writing or transitioning.

## Creating evidence

`edit-control-implementation` also permits creating evidence while the
issuing user can edit the control's implementation. Evidence creation does not
require `If-Match`, and does not make implementation or assessment claims.
Use `POST /projects/{project}/controls/{control}/evidence/uploads` with a
multipart `file` and the normal evidence metadata; `201` means the evidence
record exists, not that scanning has completed. Poll control detail until its
file status is `ready` or `error`; pending and failed files cannot be read.

Use JSON `POST .../evidence/references` for `external_reference` (with
`external_reference`) or `text_reference`, and JSON `POST .../evidence/links`
for an existing uploaded file, register, library document or Bitbucket file.
For uploaded reuse, supply `source_control_id` and `source_evidence_id`; the
source must be a ready file in the same project. `GET .../evidence/link-options`
lists permitted registers, library documents and verified Bitbucket connections
without creating records. Links repeated to the same destination/source return
`200` and `already_linked: true`, preserving the existing metadata.

Uploads and references are not idempotent. If a response is lost, retrieve the
control evidence inventory before deciding whether to retry; do not blindly
repeat an upload or reference creation.

Every upload is scanned and content-validated independently. SecBoost may then
reuse a released physical file only within the same project when its checksum,
size, detected MIME type, validation profile, filename, uploader and image
dimensions (where applicable) match. This is a storage optimisation: agents
should still upload once and explicitly link the ready evidence when reuse is
intended.

A confirmed malicious verdict blocks existing uploaded evidence with the same
SHA-256 across projects. Associations, stored bytes and assessment history remain
retained, but affected files become `error` and cannot be downloaded or linked.
Later clean uploads or organisation imports of that checksum stay blocked while
the malicious scan record is retained. Indeterminate results do not revoke older
files. There is no automatic unblocking or retrospective rescan on scanner updates.

For linked live evidence, follow an advertised `evidence_items[].links.content`
or `.links.download` URL. These reads require `view-controls` plus the issuing
user's current source permission. Content responses are live, use
`Cache-Control: no-store`, and Bitbucket responses identify the exact resolved
commit and SHA-256 returned. Do not send Bitbucket credentials: SecBoost uses
the project's configured connection server-side.

`text_reference` evidence is already self-contained: read its stored note from
`evidence_items[].description`. It intentionally has no `content` or `download`
link. `external_reference` items likewise retain their stored URL and access
instructions in the inventory; SecBoost does not fetch arbitrary external URLs.
Use each item's `content_representation` as the machine-readable instruction:
`inline`, `external_access`, `content_endpoint`, `download_endpoint`, or
`content_and_download`. `content_endpoint` has `links.content`;
`download_endpoint` has `links.download`; `content_and_download` has both.
`inline`, `external_access`, and `unavailable` have neither link. Follow only
advertised endpoint links; `unavailable` means no supported API representation
is currently available.

For Bitbucket evidence, `409 evidence_content_too_large` and
`409 unsupported_content_representation` include `links.download`. This is an
authorised hint only: download has its own configured limit and may still fail.
Repository-provider failures are safe problem responses: `504` is a timeout,
`503` is temporary unavailability or throttling, and `502` is another upstream
failure. Do not retry a credential rejection unchanged.

Use `assessment_preparation_readiness` to identify missing project proposals.
With assessment-read authority, `assessment_matrix_completeness` identifies the
exact SSP Annex and CCM conclusion fields which are still incomplete. A value
of `Not Assessed` is incomplete. Submission or completion also requires an
overall outcome and overall rationale/evidence references. `Common Control` is
valid only for SSP control rows, not principles or the CCM. Ineffective, No
Visibility and Not Implemented overall outcomes require a Critical, High,
Medium or Low finding severity.

```http
PATCH /api/v1/projects/01PROJECT/controls/01CONTROL/implementation
Authorization: Bearer <token>
Accept: application/json
Content-Type: application/merge-patch+json
If-Match: "c4a55d3b0982be28a3e1ffb541507dd5c5789d20da47d1bc00916ee5e885a8ad"

{
  "implementation_status": "Effective",
  "implementation_description": "The control is operating as designed with internal implementation detail.",
  "implementation_summary": "The organisation states that the control has been implemented and is operating as designed."
}
```

Use only a transition ID returned by the refreshed detail response:

```http
POST /api/v1/projects/01PROJECT/controls/01CONTROL/transitions
Authorization: Bearer <token>
Accept: application/json
Content-Type: application/json
If-Match: "d725c75e2a6ffc577e3a5bbf40c66f364cc6a93ccd0539c0951cc06a3106bcad"

{
  "transition": "editing_to_ready_for_assessment"
}
```

Transitions into `Assessed` capture immutable evidence versions. If evidence
cannot be captured or verified, the first attempt without acknowledgement
returns `evidence_acknowledgement_required`. Review the evidence warning, then
retry deliberately with the same current ETag and:

```json
{
  "transition": "assessing_to_assessed",
  "acknowledge_evidence_warnings": true
}
```

For one of the four approval/rejection decisions whose advertised `comment`
value is `required`, provide a non-blank rationale to persist atomically:

```json
{
  "transition": "approving_edit_to_ready_for_assessment",
  "comment": "Reviewed against the implementation evidence."
}
```

Comments remain unsupported for every other transition and are rejected rather
than silently discarded. Approval capabilities are advisory; immediately
before changing state, the server rechecks the token permission, live user
permission, workflow configuration, gates and ETag while holding the row lock.

## Safe recovery

Errors use `application/problem+json`. Branch on `status` and `code`, rather than
matching human-readable `detail`. Retain `X-Request-ID` when requesting support;
omit credentials and sensitive request/response bodies.

- `401`: check expiry, revocation, account status and the credential type required
  by the endpoint. Replace an invalid credential through the UI. Never log the token.
- `403`: do not retry unchanged; the selected/live permission, execution mode, project editability,
  or workflow state does not permit the action.
- `404`: verify the returned IDs, parent/child relationship and current target
  access. Inaccessible resources can return 404. Do not probe other identifiers.
- `412`: retrieve detail again, reconcile changes, and retry with the new ETag.
- `428`: repeat the mutation with the ETag from a current detail response.
- `422`: correct only the JSON Pointer properties reported in `errors`.
- `429`: wait for `Retry-After`, then retry with bounded exponential backoff.
- `transition_not_available`: retrieve detail and choose from the newly returned
  transitions. If none remain, stop.
- `transition_prerequisite_missing`: update the safe field pointers identified
  by the problem response, then retrieve detail before retrying.
- `assessment_matrix_incomplete`: inspect `assessment_matrix_completeness`,
  update the listed fields plus the overall outcome and rationale, then retry.
- `evidence_acknowledgement_required`: inspect the evidence warning and retry
  with explicit acknowledgement only when the caller is authorised to accept it.

A lost response to a transition is ambiguous. Retrieve the control and inspect
its current state; do not automatically replay the transition. This is
especially important for approval decisions because a successful response loss
may still have created a decision record.

The complete contract is [OpenAPI 3.1](../openapi.json).

Use `GET /api/v1/organisations/{organisation}/setup-options` with `view-projects`
plus `manage-projects`
to discover the configured ISM release, licensed classification values, scope
choices and published provider assurance metadata. Prepare organisation and system context through their setup endpoints or the UI
before project creation.
A changed E8 target also requires `project_edit_reason`. Scope saves replace the
supplied scope data; omitted provider selections are preserved, while `[]` clears
them with `manage-projects` and the normal project-update policy.

Organisation tokens use `view-projects` for safe project identity/status and
`view-projects` plus `manage-projects` for full setup discovery, creation,
configuration and population. Setup-only fields are redacted without selected/live
management authority. Full organisation/system identity, canonical context and
logos separately require `manage-org-hierarchy`. Minimal system discovery accepts
that hierarchy permission or project view plus management. All operations retain
current organisation access and their normal policy/workflow checks. Read-only
execution must be disabled for writes. Retired development tokens must be reissued.

## Prepare the organisation

After a human creates the organisation and grants access, request an organisation
key with the capabilities needed for your task. Use `manage-org-hierarchy` for
identity/context/logo reads and mutation-enabled edits/uploads.
Request register read/write permissions only when needed; management requires read.
Hierarchy authority is not a blanket dependency for unrelated shared/project work.

1. GET `/organisations/{organisation}` and PATCH allowlisted identity fields with
   its ETag. Omit unchanged fields; nullable contact fields and notes can be cleared.
   Slug changes use the normal UI allocator; an omitted slug remains unchanged.
2. GET `/organisations/{organisation}/context` for the schema and ETag. PATCH
   selected `data` fields with documented values. Null clears a nullable field;
   `data:null` is rejected. Organisation `outsourced_delivery` and system
   `internet_facing_services` / `third_party_privileged_access` accept the strings
   `"yes"` or `"no"` (or null to clear), not JSON booleans. Review dates are checked
   against the merged profile. API discovery represents these fields as
   `single_select` with explicit string `yes`/`no` options.
3. Use the [system setup routes](#system-setup-and-shared-data) to create a system
   and prepare its context. Then [create/configure the project](#organisation-project-setup)
   and explicitly request population.
   All normal readiness gates apply; no importer or prior project UI visit is needed.
4. For a logo, GET `/logo`, then POST a multipart `file` using its ETag and a
   stable `Idempotency-Key`. A `201` response means accepted into quarantine;
   poll GET `/logo` until ready or error. Download using the returned API URL.
   Preserve the same bytes/name/type/size when retrying. Receipts expire after
   24 hours; after that, inspect the current state before starting fresh work.
5. Discover `/registers` and use the returned organisation-owned manual definitions
   and columns for bounded row creation/PATCH. Follow the existing receipt/ETag
   rules. System contacts use the system contact endpoints. Organisation rows
   do not support selecting contacts, controls or documents.

Organisation creation, access/role changes, archive/delete/restore, generated-row
writes, review stamping, logo delete/cancel and provider publication are excluded.

For evidence removal, DELETE
`/projects/{project}/controls/{control}/evidence/{evidence}`. For processing
cancellation, POST the same path plus `/cancel-processing`, using JSON `{}`.
Both require selected/live `view-controls` and `edit-control-implementation`,
mutation-enabled execution, an editable project and an Editing control. Source
boundaries and library view authority apply; register evidence additionally needs
selected/live `view-registers`. Supply the current metadata item ETag in `If-Match`
(428 missing, 412 stale); no caller properties are accepted.

Removal unlinks the association, preserves assessment snapshot facts, marks drift,
retains shared/snapshot files and leaves canonical registers, library documents and
repository sources intact. Orphan bytes are cleaned after commit. Unreferenced
pending uploads are rejected durably and quarantine cleanup is queued after commit.
Cancellation only accepts processing uploaded evidence (409 otherwise). Either
operation returns 409 if a processing file is needed by another item or snapshot.
A successful 200 returns `data.id` plus `deleted: true` or `cancelled: true`.
No creation receipt applies; repeat requests return 404. After an uncertain response,
read the original metadata item and reconcile before retrying.

## Applicability and project health

Project responses include `health`, which describes ongoing issues independently of setup `readiness`. Setup can be ready while a diagram is missing. Health uses the UI evaluator; its warning count counts issues, not affected controls. Applicability counts and recalculation status require selected and live `view-controls` authority. API health omits UI action URLs and raw context, evidence and assessment data.

Organisation-bound project detail and discovery responses also include shared health. Organisation credentials cannot select project control permissions, so their health excludes applicability counts, conflicts and recalculation details even if the actor can read controls in the UI. Use an authorised project credential for control-level health and decisions; organisation credentials still report setup/evidence issues such as a missing architecture diagram. Health does not grant access to restricted setup fields.

Controls expose `applicability_context` in lists and details. `origin` distinguishes default inclusion, initial recommendation, explicit user choice, classification exclusion and unknown historical provenance. Default inclusion does not imply human review or compliance. Uncertain recommendations do not require confirmation. Use `filter[applicability_attention]=conflicts` or `suggested_exclusions` for supported, current, unacknowledged disagreements.

An authorised implementation PATCH that explicitly supplies `applicability` records an applicability decision even when the value stays the same. Supply `applicability_comment` as the rationale for an exclusion or for keeping a choice that differs from a supported recommendation. An unrelated implementation PATCH does not acknowledge a disagreement. The decision is recorded separately from SecBoost's recommendation, preserving both histories. Existing editing permissions, workflow, mutation-enabled mode and current `If-Match` remain required; inspect `allowed_actions` before attempting a write. `ec` remains server-owned.

Scope and context changes queue recommendation recalculation, preserving stored applicability. Inspect `health.recalculation` and `stale_recommendations`; queued or completed alone is not proof of current results. Structured scope fields drive rules; changing narrative artefact prose or `assessment_scope.data.notes` alone does not change those facts. Historical acknowledgements remain valid across notes-only edits. A kept decision remains acknowledged on an unchanged rerun, but changed facts or rules can reopen a supported conflict. Recommendations and their freshness participate in the control ETag, so retrieve a new ETag after recalculation.


To upload a project network diagram, save the assessment scope first. GET the
organisation-bound project or `/artefacts` to obtain its setup ETag, then POST
multipart `file` to
`/organisations/{organisation}/systems/{system}/projects/{project}/artefacts/network-diagram`
with `If-Match` and a mutation-enabled organisation credential selecting
`view-projects` and `manage-projects`. Accepted formats are PNG, JPEG and WebP,
at most 10 MiB. `202` means queued for scanning, not ready for document use.
Poll `/artefacts` and inspect `assessment_scope.network_diagram.processing`,
`status` and `error`; then re-read project `health` to check warning clearance.


Ready diagram metadata now returns an API `url` for the caller's credential type:
organisation credentials use the nested project `/artefacts/network-diagram/file`
route; project credentials use `/projects/{project}/artefacts/network-diagram/file`.
Project inputs/list responses include diagram metadata in `assessment_scope_artefacts`.
GET that URL with the same bearer credential. Both routes require selected/live
`view-projects` and normal bound project-read access, support read-only execution,
and stream only ready files with private, no-store headers. Pending, failed or
missing diagrams return 404. Compare downloaded bytes with the recorded checksum.

Use `health.applicability_summary.applicable` for the number of `y` controls.
Conflicts overlap applicability outcomes: for example, 100 applicable, 5 not
applicable and 10 classification-excluded controls total 115, even if 20 of the
applicable controls have conflicts. Do not add the conflict count to that total.
