---
title: "Update a defect"
method: PATCH
path: "/defects/{defectId}"
tags: ["Assets"]
---

# Update a defect

`PATCH /defects/{defectId}`

Partial update of one or more mutable fields:
  * `workDoneAt` -- Unix ms to mark the defect as addressed,
    null to clear the mark (undo).
  * `defectPriority` -- 1..4. Sending this field always sets
    `prioritySource='manual'` so a subsequent bulk schema edit
    cannot overwrite the reviewer's decision. May be sent
    standalone; sending `prioritySource: 'manual'` alongside
    is allowed but redundant.
  * `prioritySource: 'mapping'` (alone, without
    `defectPriority`) -- revert path. Re-derives
    `defectPriority` from the org's active
    `defect_priority_mappings` entry for this row's
    `(asset_type, defect_name)` tuple. 409 with
    `code='no_mapping_baseline'` if no live row in the same
    tuple currently carries a mapping-derived priority (the
    schema must be updated or the CVAT export re-run before a
    revert can succeed).
  * `reviewed` -- bool. true stamps `reviewedAt=now()` and
    `reviewedByActorId=<caller>` (the customer-portal "Reviewed
    by" mark); false clears both. The reviewer identity is taken
    from the auth context, never the body.
All writes trigger the `stream_priority_defects` database
trigger, which cascades through voltair-streams-priority to
recompute the owning asset and (if changed) site priority.

## Headers

- `Idempotency-Key` string, uuid

## Request body

- object
  - `workDoneAt` number, nullable — Unix ms timestamp. Null clears the field.
  - `defectPriority` integer — Manual priority override. Sets prioritySource='manual'.
  - `prioritySource` 'manual' | 'mapping' — Sent alone with value 'mapping' to revert a manual override (re-derives priority from the active mapping). Sent with defectPriority and value 'manual' is allowed but redundant -- defectPriority alone implies manual.
  - `reviewed` boolean — Customer-portal "Reviewed by" mark. true stamps reviewedAt + reviewedByActorId from the auth context; false clears both.

## Response `200`

Success

- object
  - `data` Defect, required — Merged defect. One row per (assetInspectionId, defectName) at a time. Per-CVAT-shape evidence (bbox, evidence photo, original per-shape description) lives on DefectEvidence and is exposed via `expand[]=evidence` (or, on listInspections, `expand[]=asset.defects.evidence`). The `description` field on this schema is the denormalized "longest non-empty live evidence description" cache maintained by the CVAT export.
    - `id` string, uuid, required
    - `organizationId` string, uuid, required
    - `assetId` string, uuid, required
    - `assetInspectionId` string, uuid, required
    - `assetType` string, required
    - `defectName` string, required
    - `defectPriority` integer, required — 1 = highest severity, 4 = none/normal
    - `prioritySource` 'mapping' | 'manual', required — Where `defectPriority` came from. Bulk schema edits only touch rows where `prioritySource='mapping'` so reviewer manual overrides survive.
    - `defectPriorityMappingId` string, uuid, nullable — Active defect_priority_mappings.id at the time defectPriority was last derived. Informational/audit; nullable on legacy rows imported before this column existed.
    - `workDoneAt` number, nullable, required — Unix ms timestamp marking this defect as addressed in the field. Null while unaddressed. When non-null, the defect is excluded from the asset priority rollup.
    - `reviewedAt` number, nullable — Unix ms timestamp the customer marked this defect reviewed (the 'Reviewed by' mark). Null when not reviewed. Set/cleared via PATCH /defects/{id} { reviewed }.
    - `reviewedByName` string, nullable — Display name of the actor who set the current reviewed state. Null when not reviewed; empty string for api-key actors with no user row. Resolved from reviewedByActorId; present on list/expand reads, omitted on the mutation response.
    - `description` string, nullable — Longest non-empty `description` across the parent's live DefectEvidence rows. Maintained by the CVAT export — the API and UI read this column directly rather than aggregating across evidence on every fetch.
    - `deletedAt` number, nullable, required
    - `createdAt` number, required
    - `evidence` DefectEvidence[] — Populated only when `evidence` is requested via expand[]. Otherwise omitted.
      - `id` string, uuid, required
      - `defectId` string, uuid, required
      - `cvatShapeUid` string, nullable — Stable id `<cvat_task_id>:<cvat_shape_id>` of the source CVAT shape. NULL on non-CVAT-sourced rows (none today).
      - `evidenceMediaId` string, uuid, nullable — Original (uncropped) mission_media row that the lineman annotated.
      - `bbox` object, nullable — Bounding box in evidence_media_id pixel coordinates: {"x1": int, "y1": int, "x2": int, "y2": int}.
      - `description` string, nullable — Free-form text the lineman attached to this specific CVAT shape (Comment attribute on regular labels, Description on the Other catch-all).
      - `reviewUncertain` boolean, required — True when this shipped evidence crop came from QA consensus=agreed_uncertain. The customer platform rolls this up to an Uncertain tag on the merged defect.
      - `deletedAt` number, nullable, required
      - `createdAt` number, required
  - `transactionId` string, uuid, required

## Other responses

- `400` — Bad request or validation error
- `401` — Authentication required
- `403` — Insufficient permissions
- `404` — Resource not found
- `429` — Rate limit exceeded
- `500` — Internal server error

---

[API](https://skmtc.net/voltairlabs/apis/voltair-api.md) · [All operations](https://skmtc.net/voltairlabs/apis/voltair-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/voltairlabs/voltair-api/revisions/be08f4f706f5/schema)
