Set Verdict Override

Create or replace the finding on one (axis, subject_type, subject_id). That triple is the key — a second PUT with the same triple replaces the finding rather than adding another, so this endpoint is safe to retry.

One record can hold many findings at once: a compliance override on plot A, an exclusion attestation on plot B, and a legality clearance at record grain all coexist. They are independent rows.

Request body

{
  "axis":                  "plot_compliance" | "legality" | "exclusion", // required
  "subject_type":          "plot" | "entity" | "record",   // default "plot"
  "subject_id":            "uuid",                          // required
  "narrative":             "max 5000 chars",                // required
  "basis":                 "max 200 chars",                 // required on exclusion; optional elsewhere
  "verdict_override":      "compliant" | "non_compliant" | null, // optional
  "exemption_driver":      "mine" | "settlement" | ... | null, // optional; plot_compliance + compliant only
  "document_attachment_id":"uuid" | null,                   // optional
  "evidence_urls":         ["https://...", ...],            // optional, max 50
  "valid_from":            "ISO-8601" | null,               // optional
  "valid_to":              "ISO-8601" | null,               // optional
  "reason":                "max 2000 chars"                 // required
}

narrative and reason are different things and both are required. The narrative is the operator's statement, and it is printed in the audit-trail document for an inspector to read. The reason explains why you are making this API call, and it goes to the mitigation event log. A narrative outlives the call; a reason describes it.

Picking subject_type and subject_id

Axissubject_typesubject_id
plot_complianceplotthe plot's id
exclusionplotthe plot's id
legalityrecordthe record's own id

subject_type defaults to plot, so a legality clearance must set it explicitly. A legality finding written at plot grain is accepted by the API but the report looks for it at record grain and will not display it.

verdict_override — override or attestation

  • Set to compliant / non_compliant: the subject now reads that way, whatever Epoch computed. This is an override.
  • Omitted or null: the computed verdict stands and you have recorded a reason against it. This is an attestation.

The exclusion axis never carries one: there is no computed verdict to overturn. That does not make it inert — writing the finding is what removes the plot from the filing. Sending verdict_override on this axis answers 400.

exemption_driver: record an exemption

A plot_compliance override to compliant can name a non-agricultural cause: settlement, infrastructure, mine, water_body, natural_hazard, dam or linear_clearing. The plot then reads exempt with that cause, not plain compliant. This is how an operator accepts an adjudicator suggestion whose suggested_outcome is exempt. Sending it on another axis or with any other verdict answers 400. Omitted keeps the stored value; null clears it. Removing the override withdraws the exemption too.

basis

On exclusion the basis is required and must be one of the Article 10 negligible-risk grounds:

ValueMeaning
negligible_after_mitigationrisk mitigated to negligible
pre_cutoff_productionproduced before the 31 Dec 2020 cut-off
outside_supply_basenot part of this consignment's supply base
otheranything else — explain it in the narrative

de_minimis_area is retired and answers 422 on a new write: the EUDR has no de minimis area (Article 2(13)). Findings that already carry it still read and render.

On plot_compliance and legality the basis is optional free text (svlk, flegt, a scheme name). The vocabulary is closed for exclusion only, because that is the one axis where the regulation names the grounds.

valid_from / valid_to

Bound how long the finding applies. Omit both for a finding that does not expire.

Use them for anything issued per shipment — FLEGT and SVLK certificates especially. When the clock passes valid_to the finding stops counting with no further call: the subject reverts to its computed verdict, leaves ?include=compliant, and trips the submit gate again. The expired finding stays readable here and stays printed in the audit trail, marked expired.

Refused on exclusion with a 400. An exclusion is a scope decision, not evidence with a shelf life. An expiring one would put a plot back into a filing with nobody acting and no audit event. DELETE the finding to re-include the plot.

document_attachment_id

Supply it and provenance becomes document; the audit trail then prints the file's name beside the attestation. Upload the file through the Attachments endpoints first.

AxisAccepted categories
plot_compliancesupporting_document, certificate
legalitylegality_evidence, certificate
exclusionsupporting_document, certificate, due_diligence_statement

An id that does not exist, or a category the axis does not accept, answers 400.

evidence_urls

Replaces the list wholesale, max 50. Omit to leave the existing list untouched; send [] to clear it.

Every optional field works this way — absent means "no instruction", not "clear it". An edit that only corrects the narrative keeps the evidence, the attachment, the basis, the verdict and the validity window. Send an explicit null (or []) to remove one.

What changes downstream

  1. The submit gate. A plot_compliance override to compliant clears that plot from the plots_non_compliant blocker, so the record can be filed. legality warns only. exclusion removes the plot from the filing altogether.
  2. The report and the GeoJSON. The plot register shows the effective verdict with provenance: "override", and the TRACES-bound report.geojson follows it.
  3. The audit-trail PDF. A plot_compliance override adds a card under Plots Cleared by Operator Override; an exclusion attestation adds one under Excluded Plots with Negligible-Risk Attestation; a legality clearance renders inside the Article 9(1)(h) section. Each carries the basis, the document name, the validity window, the narrative and the signatory.
  4. The audit log. A config.finding_set event with your reason as payload.note, the acting user and organization, and — for a plot-grain finding — the plot as target_plot_id.

The computed signal is never replaced

Both readings stay on the page. A plot flipped to compliant still shows the hectares of loss that triggered the alert, and the legality section keeps its risk badge and every law card.

A legality clearance that moves the verdict also prints the move, so the assessed position cannot be hidden by the override:

Status: High (assessed) → Low (operator override)

Each side is labelled with who says it — the second value is the operator's position, never Epoch's. The line appears only on a real flip; a finding that merely attests prints no transition, because it changed nothing.

Errors

StatusCodeWhen
400BAD_REQUESTUnknown axis; missing narrative or reason; exclusion with a basis outside the vocabulary; unknown document_attachment_id or a category the axis rejects; subject_id not a plot in the current version; invalid evidence URL
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENKey lacks eudr:write
404NOT_FOUNDRecord is not visible to the caller
409CONFLICTThe version is filed to TRACES and frozen

Required scope: eudr:write