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.9 listEquipmentImageLinks
GET /api/equipment/image-links
Every active image-to-asset link in one campaign, keyed by image id, so a candidate queue can group by equipment without one call per image.
Why it exists. Give a caller every image-to-asset attribution in a campaign in one request, so a candidate queue can group by equipment without one call per image.
Bulk image -> asset attribution for one campaign, resolved from image_asset_link (materialized, human-correctable geo link) exclusively – never the live image_asset_proximity view, which is a separate, unreviewed inference (decided 2026-08-31, docs/proposals/20260831_what_the_connector_cannot_answer.md, D4). An image with no active link is simply absent from the returned map, never guessed. An image can carry more than one active link; the best-informed is returned – by method (ocr_asset_tag, ocr_asset_tag_pair, pose_frustum, geo_proximity_5m, geo_nearest_centroid) and then by distance_m – the same order GET /images/{id}/asset-link uses for one image, with method returned so a reviewer can see why.
Authentication: CookieAuth, SupabaseAuth
Returns — links
Field names as the handler returns them; the source states no types for them.
Effect — reads datasets · equipment · image_asset_link
Parameters
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
dataset_slug |
query | no | string |
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
Image-to-asset links, keyed by image id. Empty when the slug does not resolve to a campaign the caller can see. | |
400 |
dataset_slug is required. | Error |
401 |
Unauthorized. | Error |
500 |
Internal server error. | 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 |
16.0.12 getImageAssetLink
GET /api/images/{id}/asset-link
Shows which asset a photo is linked to, how that was established, and how confidently.
Why it exists. ‡ Return the equipment item this image is nearest to, so the viewer can tell the user which asset they are looking at.
Called by — modules/gallery/overlays/components/asset-link-chip.tsx
Reached from — /datasets/<slug> · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer
The image’s best-informed active asset link (equipment tag, distance, confidence, method), or null. When an image carries several active links the order is by method – a tag read off the equipment (ocr_asset_tag, ocr_asset_tag_pair) beats the camera having been pointed at it (pose_frustum), which beats the camera having been near it (geo_proximity_5m, geo_nearest_centroid) – and only then by distance. Requires membership of the org owning the image’s dataset.
Authentication: CookieAuth, SupabaseAuth
Returns — link
Field names as the handler returns them; the source states no types for them.
Effect — reads equipment · image_asset_link
Parameters
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
id |
path | yes | string (uuid) |
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
Best-informed link or null. | |
401 |
Unauthorized. | Error |
403 |
Forbidden. | Error |
404 |
Image not found. | Error |
500 |
Internal server error. | Error |
16.0.13 deleteImageAssetLinks
DELETE /api/images/{id}/asset-link
Unlinks a photo from its asset, keeping an audit record of who removed it and why.
Why it exists. ‡ Break the link between an image and an equipment item, for correcting a proximity match that put the image against the wrong asset.
Called by — components/workspaces/asset-images.tsx · modules/gallery/overlays/components/asset-link-chip.tsx
Reached from — /datasets/<slug> · /w/data-explorer/anomalies/map · /w/data-explorer/assets · /w/integrity/assets · /w/integrity/evidence-explorer
Soft-removes the image’s active asset link(s) by setting status=removed with an audit trail (removed_by/removed_at/removed_reason). Pass linkId to remove one specific link; omit to remove all active links.
Authentication: CookieAuth, SupabaseAuth
Returns — removed
Field names as the handler returns them; the source states no types for them.
Effect — writes (update) image_asset_link · images
Parameters
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
id |
path | yes | string (uuid) |
Request body
| Field | Type | Required |
|---|---|---|
reason |
string | no |
linkId |
string (uuid) | no |
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
Number of links removed. | |
401 |
Unauthorized. | Error |
403 |
Forbidden. | Error |
404 |
Image not found. | Error |
500 |
Update failure. | Error |