Patch DDS Config

Apply incremental changes to a dds_record's exclusion + justification config — without touching plots you don't mention. Useful when iterating on a review: add one exclusion, drop another, bump a justification text, attach an extra evidence URL — without having to resend the full state every time.

Note — compliance verdict overrides: this bulk endpoint does not accept compliance_override on set_justifications. For verdict overrides use the per-plot Set Verdict Override endpoint, which writes the override + narrative + audit reason in a single PUT. Updates here that touch a plot's justification text leave any existing override on the row untouched (preserve semantics).

Path params

ParamDescription
recordIdDDS record id

Request body

Every key is optional, but at least one must be set (else 400).

{
  "set_justifications": {
    "<plot-uuid>": { "text": "max 5000 chars" }
  },
  "remove_justifications": ["uuid", ...],  // drop these justifications entirely
  "add_evidence_urls": {
    "<plot-uuid>": ["https://...", ...]    // append (deduped) per plot
  },
  "remove_evidence_urls": {
    "<plot-uuid>": ["https://...", ...]    // remove specific URLs per plot
  }
}

Operation order (within one PATCH)

Operations run in a single transaction. Order is fixed so a caller can swap roles within the same request:

  1. remove_justifications → justification rows deleted (this also drops any compliance_override on the row)
  2. set_justifications → justifications upserted (insert or update text). On update, any existing compliance_override on the row is preserved untouched; on insert, the new row's compliance_override defaults to null. Use PUT /verdict-overrides (axis plot_compliance) to set, change or clear the override. That endpoint is a FULL replace — it writes the finding row you send, so include every field you want to keep.
  3. Per plot in add_evidence_urls ∪ remove_evidence_urls: read existing array → drop remove URLs → append add URLs (deduped) → write back

Adding an exclusion that's already present, removing one that isn't, or setting a justification with the same text are all idempotent — re-sending the same PATCH is safe.

Validation rules

  • Visibility: 404 when the record's organization_id isn't in the caller's effectiveOrgIds.
  • Plot membership: every UUID in any of the body fields must appear in the record's current version snapshot. The union across all six fields is checked in one pass; the 400 response lists the offending ids.
  • At least one operation: empty body → 400 BAD_REQUEST.
  • Evidence URLs require a justification: add_evidence_urls or remove_evidence_urls for a plot that has no justification is a silent no-op. Set the justification first (or in the same PATCH) for the URLs to land.

Response (200)

Returns the full post-mutation config state — the current exclude_plots array and justifications map (each entry with text, evidence_urls, and compliance_override) for the record's current version.

add_excluded / remove_excluded were removed (EPO-1983). An exclusion is now a finding, so use PUT / DELETE /verdict-overrides with axis: "exclusion". They are refused by name with a 400 rather than silently dropped: this body is not .strict(), so an ignored add_excluded paired with a justification edit would answer 200 having done half the work, and the caller would file a record still holding the plot they believed they had excluded.

Errors

StatusCodeWhen
400BAD_REQUESTEmpty body, body failed Zod validation, or a referenced plot id is outside the record's current version
401UNAUTHORIZEDMissing/invalid API key
404NOT_FOUNDRecord isn't visible to the caller

Required scope: ingestions:write