16  Equipment

13 operations.

16.0.1 listDamageMechanisms

GET /api/damage-mechanisms

Lists the API-571 damage mechanisms, so an engineer can ground a finding in a recognised industry mechanism rather than only in a class somebody already drew.

Why it exists. ‡ Offer the API-571 damage mechanism catalog, so an engineer can ground a finding in a recognised industry mechanism rather than only in a class somebody already drew on a photograph.

Called by — components/workspaces/condition-class-picker.tsx

Reached from — /w/data-explorer/assets · /w/data-explorer/insights · /w/integrity/assets · /w/integrity/investigate

The API-571 catalog — the other half of the vocabulary a finding can be grounded in. A finding carries exactly one grounding (edm_one_grounding): an API-571 mechanism, or an annotation_categories observation. listAnnotationCategories has offered the second since WP-3; this offers the first. Unlike the annotation vocabulary, this carries no tenant scope and the claim is genuine: damage_mechanisms is USING (true) because API-571 is a published industry standard whose mechanism names are not tenant data. Which assets exhibit them is tenant data, and that is listEquipmentDamageMechanisms. Returns the columns a picker needs — the full API-571 entry (description, prevention, critical factors) is deliberately not included, since each is a paragraph and sending them all would multiply the payload to render a dropdown.

Authentication: CookieAuth, SupabaseAuth

Returns — limit · truncated · mechanisms

Field names as the handler returns them; the source states no types for them.

Effect — reads damage_mechanisms

Parameters

Name In Required Type Notes
limit query no integer

Responses

Status Meaning Body
200 The API-571 damage mechanism catalog.
401 Not authenticated. Error
500 Server error. Error

16.0.2 listEquipment

GET /api/equipment

Lists the organization’s equipment with finding and photo counts.

Why it exists. ‡ List an organisation’s equipment with a count of open findings and linked images against each, so the asset register shows where attention is needed.

Called by — components/workspaces/assets-register-client.tsx · lib/mcp/manifest.ts · modules/gallery/overlays/components/image-asset-tags.tsx

Reached from — /datasets/<slug> · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Equipment rows in the caller’s organization(s), each with a finding count and a nearby-image count (image_asset_link rows, auto-suggested by proximity — not a person’s confirmation; deduplicated by the underlying photo, so a campaign that shares an image with another doesn’t count it twice), plus a capture-time-clustered distinct_visits_count so a drone’s near-continuous dwell burst near one asset doesn’t read as that many separate looks. Sorted by tag. No pagination.

Authentication: CookieAuth, SupabaseAuth

Returns — assets

Field names as the handler returns them; the source states no types for them.

Effect — reads equipment · equipment_damage_mechanisms · image_asset_link

Parameters

Name In Required Type Notes
organization_id query no string

Responses

Status Meaning Body
200 Asset register.
401 Unauthorized. Error
500 Internal server error. Error

16.0.3 listEquipmentDamageMechanisms

GET /api/equipment-damage-mechanisms

For an organisation, every finding raised against its assets; for one photo, the findings it evidences and its geo link; for one campaign, which of its images are already decided.

Why it exists. ‡ List the findings raised against an organisation’s assets, so the integrity worklist can show what is outstanding, and (datasetSlug mode) let a candidate queue compute its review states in one request.

Called by — app/(authenticated)/w/integrity/investigate/investigate-client.tsx · lib/workspaces/queries/candidates.ts

Reached from — /progress · /w/data-explorer/anomalies · /w/integrity/campaigns · /w/integrity/evidence-explorer · /w/integrity/insights · /w/integrity/investigate

Three independent modes. organizationId — every finding raised against the organisation’s assets, newest first (added 2026-08-31, since a finding is a claim about an asset and an asset belongs to an organisation, not a campaign); takes precedence over the other two. imageId — findings citing that image as evidence, its high-confidence nearest-asset geo link (image_asset_proximity view), and findings on that geo-linked asset. datasetSlug — a decided-state map for every image in the campaign (new / confirmed / dismissed), so a candidate queue can compute its states in one request instead of one per candidate. Exactly one of organizationId/imageId/datasetSlug is required. Requires membership of the org owning the image’s, campaign’s, or organisation’s dataset.

Authentication: CookieAuth, SupabaseAuth

Returns — findings · scope · geoLink · geoFindings · decided

Field names as the handler returns them; the source states no types for them.

Effect — reads annotation_categories · damage_mechanisms · datasets · equipment · equipment_damage_mechanism_decisions · equipment_damage_mechanisms · image_asset_proximity · images · profiles

Parameters

Name In Required Type Notes
organizationId query no string (uuid)
imageId query no string (uuid)
datasetSlug query no string

Responses

Status Meaning Body
200 For organizationId — the organisation’s findings, newest first, with scope organization; geoLink/geoFindings are omitted. For imageId — findings and geo link. For datasetSlug — decided.
400 organizationId, imageId or datasetSlug is required. Error
401 Unauthorized. Error
403 Forbidden. Error
500 Query failure. Error

16.0.4 createEquipmentDamageMechanism

POST /api/equipment-damage-mechanisms

Creates a new finding on an asset — grounded in exactly one of an API-571 damage mechanism or an inspection observation — stamped to the engineer who raised it. A second finding on the same asset and grounding is allowed.

Why it exists. ‡ Let an integrity engineer raise a finding on an asset — the claim that a piece of equipment has a problem — from the conditions its photographs actually show.

Called by — components/notes/components/promote-note-dialog.tsx · components/workspaces/new-finding-dialog.tsx

Reached from — /w/data-explorer/assets · /w/data-explorer/insights · /w/integrity/assets

Records a new finding on an asset — the human-created counterpart to the rows the ai/ pipeline populates. A finding is grounded in exactly one of an API-571 damage_mechanism_id or an inspection observation_category_id; the database enforces the XOR (edm_one_grounding). identified_by is stamped server-side to the creating engineer, never accepted from the body. The finding also stores condition_label, resolved server-side from whichever catalog grounds it, so the saved label and the displayed one cannot disagree; a grounding that names no row the caller can see is a 400 rather than a foreign-key 500. Authorized by the database (CONTRACT-API-001): edm_org_insert admits an organization’s admins and members and excludes viewers. A second finding on the same (equipment, grounding) is ALLOWED — it was a 409 until 2026-08-25, when D4-D of the free-findings proposal demoted the class from identity to classification and migration 20260823140000 dropped the unique constraint behind it, so two findings may share a class and each carry its own evidence.

Authentication: CookieAuth, SupabaseAuth

Returns — finding

Field names as the handler returns them; the source states no types for them.

Effect — writes (insert) annotation_categories · damage_mechanisms · equipment_damage_mechanisms

Request body

Field Type Required
equipment_id string (uuid) no
damage_mechanism_id string (uuid) no
observation_category_id string (uuid) no
credibility string — one of possible, confirmed, ruled_out no
evidence_notes string no
evidence_image_ids array of string (uuid) no

Responses

Status Meaning Body
201 The created finding.
400 Invalid equipment_id/credibility/evidence_image_ids; not exactly one of damage_mechanism_id/observation_category_id was provided; or the grounding names a row the caller cannot see. Error
401 Unauthorized. Error
403 Forbidden — caller lacks the member/admin role in the owning organization. Error
500 Insert failure. Error

16.0.5 amendEquipmentDamageMechanism

PATCH /api/equipment-damage-mechanisms/{id}

Corrects a finding after it was raised — amends the engineer’s notes, or moves it to the condition class it should have claimed — recording what it said before in an append-only edit log.

Why it exists. ‡ Correct a finding after it was raised — fix its notes, or move it to the condition class it should have claimed — keeping who changed it and what it said before.

Called by — components/workspaces/finding-correction-form.tsx

Reached from — /w/data-explorer/assets · /w/integrity/assets · /w/integrity/investigate

Corrects a finding. Amends evidence_notes and/or re-grounds the condition class the finding claims. This is NOT a decision — a finding’s verdict lives in equipment_damage_mechanism_decisions; corrections go to the sibling append-only equipment_damage_mechanism_edits log, with a before/after snapshot. Re-grounding sets one grounding and nulls the other (edm_one_grounding), re-resolves condition_label, and returns credibility to possible. The log is written BEFORE the row, so a caller RLS refuses changes nothing. A request that matches the finding’s current values writes neither and answers changed: false. Authorized by the database (CONTRACT-API-001): edm_org_update and edm_edits_org_insert both admit an organization’s admins and members and exclude viewers.

Authentication: CookieAuth, SupabaseAuth

Returns — finding · changed · action

Field names as the handler returns them; the source states no types for them.

Effect — writes (insert, update) annotation_categories · damage_mechanisms · equipment_damage_mechanism_edits · equipment_damage_mechanisms

Parameters

Name In Required Type Notes
id path yes string (uuid)

Request body

Field Type Required
evidence_notes string no
damage_mechanism_id string (uuid) no
observation_category_id string (uuid) no

Responses

Status Meaning Body
200 The finding after the correction. changed is false when the request matched what was already stored.
400 No field was provided; both groundings were provided; evidence_notes was neither a string nor null; or the grounding names a row the caller cannot see. Error
401 Unauthorized. Error
403 Forbidden — caller lacks the member/admin role in the owning organization. Nothing is written. Error
404 No such finding, or it belongs to an organization the caller is not a member of. Error
500 Unexpected error. Error

16.0.6 deleteEquipmentDamageMechanism

DELETE /api/equipment-damage-mechanisms/{id}

Deletes a finding that was raised in error — a test row, a duplicate, one on the wrong asset — writing a tombstone first so the register can be corrected without the correction being invisible.

Why it exists. † Delete a finding that was raised in error — a test row, a duplicate, a wrong asset — leaving a tombstone so the register can be corrected without the correction being invisible.

Deletes a finding. Administrators only. A tombstone is written to equipment_damage_mechanism_edits BEFORE the row is removed, carrying the whole finding plus the number of decisions the cascade will take with it. Authorized by the database (CONTRACT-API-001): edm_org_delete admits an organization’s admins alone. The tier is checked before anything is written — is_org_admin_secure, the same predicate the policy evaluates — because the tombstone sits at the editor tier and the delete at the admin tier, so writing first and learning the tier second would record a member’s failed attempt as a deletion that never happened.

Authentication: CookieAuth, SupabaseAuth

Returns — id · deleted · decisions_removed

Field names as the handler returns them; the source states no types for them.

Effect — writes (delete, insert) equipment · equipment_damage_mechanism_decisions · equipment_damage_mechanism_edits · equipment_damage_mechanisms

Parameters

Name In Required Type Notes
id path yes string (uuid)

Responses

Status Meaning Body
200 The finding was deleted. decisions_removed is how many verdicts the cascade took with it.
400 The id is not a uuid. Error
401 No authenticated caller. Error
403 The caller can read the finding but is not an admin of the owning organization. Error
404 No such finding, or it belongs to an organization the caller is not a member of. Error
500 Unexpected error. Error

16.0.7 decideEquipmentDamageMechanism

PATCH /api/equipment-damage-mechanisms/{id}/decision

Records a human verdict on a finding — confirm it, dismiss it, or reinstate one that was dismissed — as a row in an append-only decision log holding the deciding engineer, the time and the required reasoning. A revised verdict links to the one it supersedes; both are kept.

Why it exists. ‡ Record an engineer’s decision on a finding — confirm it, dismiss it, or reinstate one that was dismissed — with who decided and why. The point at which a machine-proposed finding becomes an engineering judgement.

Called by — components/workspaces/finding-decide-panel.tsx

Reached from — /w/data-explorer/assets · /w/integrity/assets · /w/integrity/investigate

Records a human decision on a finding. Maps confirm->confirmed, dismiss->ruled_out and reinstate->possible in credibility, and writes the deciding engineer, the time and the reasoning as a row in the append-only equipment_damage_mechanism_decisions log. A decision that revises an earlier one links to it via supersedes; both rows stay. reinstate reverses a dismissal by superseding it, returning the finding to possible (not confirmed) because reversing a dismissal says the claim deserves another look, not that it is true. identified_by records who found the finding and is never touched here. Authorized by the database (CONTRACT-API-001): edm_org_select decides visibility and edm_org_update decides the write; both admit an organization’s admins and members and exclude viewers.

Authentication: CookieAuth, SupabaseAuth

Returns — finding

Field names as the handler returns them; the source states no types for them.

Effect — writes (insert, update) equipment_damage_mechanism_decisions · equipment_damage_mechanisms

Parameters

Name In Required Type Notes
id path yes string (uuid)

Request body

Field Type Required
decision string — one of confirm, dismiss, reinstate no
reasoning string no

Responses

Status Meaning Body
200 Updated finding (credibility holds the mapped value, not the decision word).
400 Invalid decision or empty reasoning. Error
401 Unauthorized. Error
403 Forbidden. Error
404 Finding not found. Error
500 Update failure. Error

16.0.8 updateEquipmentDamageMechanismEvidence

PATCH /api/equipment-damage-mechanisms/{id}/evidence

Adds or removes the images a finding rests on, so a claim already raised can gather more support instead of being raised twice.

Why it exists. ‡ Attach further photographs to a finding already raised, so evidence can accumulate onto a claim after the fact rather than only at the moment it is made.

Called by — components/workspaces/finding-evidence-picker.tsx

Reached from — /w/data-explorer/assets · /w/integrity/assets

Adds images to, or removes them from, the set a finding rests on (evidence_image_ids). An id named in both add and remove ends up removed. When the result equals the current set the operation writes nothing and answers changed: false. Authorized by the database (CONTRACT-API-001): edm_org_select decides visibility and edm_org_update decides the write; both admit an organization’s admins and members and exclude viewers. A finding in another organization is a 404.

Authentication: CookieAuth, SupabaseAuth

Returns — finding · changed

Field names as the handler returns them; the source states no types for them.

Effect — writes (update) equipment_damage_mechanisms

Parameters

Name In Required Type Notes
id path yes string (uuid)

Request body

Field Type Required
add array of string (uuid) no
remove array of string (uuid) no

Responses

Status Meaning Body
200 The finding after the change. changed is false when the request resolved to the evidence already recorded and nothing was written.
400 Neither add nor remove given, or an id that is not a uuid. Error
401 Unauthorized. Error
403 Readable but not writable — the viewer role cannot edit a finding. Error
404 Finding not found. Error
500 Update failure. Error

16.0.10 listEquipmentPositions

GET /api/equipment/positions

Gives the 3D campaign overview the map coordinates of the organization’s geotagged equipment, with finding/photo counts for marker styling and the matching CAD-local position where known.

Why it exists. ‡ Return equipment positions for the plant view, narrowed to an organisation or to the facility a campaign’s imagery is linked to, so the 3D view can place assets in the scene.

Called by — components/workspaces/assets-register-client.tsx · hooks/use-equipment-markers.ts · lib/mcp/manifest.ts

Reached from — /datasets/<slug> · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Coordinates for placing equipment markers in the 3D scene, read from the equipment_positions view (only geotagged equipment appears). Scoped to the caller’s organization(s); org narrows to one org the caller belongs to, else dataset narrows to that campaign’s org and — when the campaign has active image↔︎asset links — to the facilities its imagery is linked to. Each row carries the same findings/image tallies as /equipment, plus the asset’s CAD-local coordinates (feet) when known.

Authentication: CookieAuth, SupabaseAuth

Returns — positions

Field names as the handler returns them; the source states no types for them.

Effect — reads datasets · equipment_damage_mechanisms · equipment_positions · image_asset_link · organization_members · pds_assets

Parameters

Name In Required Type Notes
dataset query no string
org query no string (uuid)

Responses

Status Meaning Body
200 Positions (empty when the caller has no organizations).
401 Unauthorized. Error
500 Internal server error. Error

16.0.11 getEquipment

GET /api/equipment/{id}

Shows one asset in full: its details, its engineering data sheet (lining, design conditions and nozzle schedule), its photos split into the images of this asset — those tagged as showing it, and those whose nameplate reads its tag but that no person has approved yet — and a nearby set (linked by camera position only, each with the equipment tag its placard OCR suggests), and damage findings with their photographic evidence. Each photo names the anomalies on it — the classes its boxes were drawn under, and its damage-descriptor tags — so the asset page can list what was found on the asset and near it. Data-sheet fields are null when nothing is recorded, and the panel says “Not recorded” rather than guessing.

Why it exists. ‡ Assemble everything known about one asset — its data sheet, its damage mechanisms, and the images cited as evidence — so an engineer can judge a finding without opening four screens.

Called by — components/workspaces/assets-register-client.tsx · hooks/use-equipment-detail.ts · lib/mcp/manifest.ts

Reached from — /datasets/<slug> · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer

Asset header, its engineering data sheet (lining, design conditions and nozzle schedule from the CAD/P&ID register), its images split into the images of this asset (source confirmed: carries the asset’s tag; source nameplate: an active OCR link reads the asset’s tag off its plate, not yet approved by a person, with link_id so that one link can be retracted) and a nearby set (source nearby: linked by camera position only, with link_id), in that order, each with its anomaly signals resolved, and its damage-mechanism findings each resolved to evidencing images. A finding without explicit evidence inherits up to 12 of the asset’s images (inherited_evidence true). Data-sheet fields are null when nothing is recorded — absence is never defaulted. No material field: the register’s only populated value (T100-AB106) came from a hand-authored DEXPI exemplar, not a real drawing or datasheet, so there is no source a material field could honestly cite. Requires membership of the asset’s organization.

Authentication: CookieAuth, SupabaseAuth

Returns — asset · datasheet · findings · images

Field names as the handler returns them; the source states no types for them.

Effect — reads annotation_categories · annotations · damage_mechanisms · datasets · equipment · equipment_damage_mechanism_decisions · equipment_damage_mechanisms · image_asset_link · images · pds_asset_nozzles · pds_assets · profiles

Parameters

Name In Required Type Notes
id path yes string

Responses

Status Meaning Body
200 Asset detail.
401 Unauthorized. Error
403 Forbidden — not a member of the asset’s organization. Error
404 Asset not found. Error
500 Internal server error. Error