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
subject_type and subject_id| Axis | subject_type | subject_id |
|---|---|---|
plot_compliance | plot | the plot's id |
exclusion | plot | the plot's id |
legality | record | the 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
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
exemption_driver: record an exemptionA 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
basisOn exclusion the basis is required and must be one of the
Article 10 negligible-risk grounds:
| Value | Meaning |
|---|---|
negligible_after_mitigation | risk mitigated to negligible |
pre_cutoff_production | produced before the 31 Dec 2020 cut-off |
outside_supply_base | not part of this consignment's supply base |
other | anything 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
valid_from / valid_toBound 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
document_attachment_idSupply 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.
| Axis | Accepted categories |
|---|---|
plot_compliance | supporting_document, certificate |
legality | legality_evidence, certificate |
exclusion | supporting_document, certificate, due_diligence_statement |
An id that does not exist, or a category the axis does not accept,
answers 400.
▶evidence_urls
evidence_urlsReplaces 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
- The submit gate. A
plot_complianceoverride tocompliantclears that plot from theplots_non_compliantblocker, so the record can be filed.legalitywarns only.exclusionremoves the plot from the filing altogether. - The report and the GeoJSON. The plot register shows the
effective verdict with
provenance: "override", and the TRACES-boundreport.geojsonfollows it. - The audit-trail PDF. A
plot_complianceoverride adds a card under Plots Cleared by Operator Override; anexclusionattestation adds one under Excluded Plots with Negligible-Risk Attestation; alegalityclearance renders inside the Article 9(1)(h) section. Each carries the basis, the document name, the validity window, the narrative and the signatory. - The audit log. A
config.finding_setevent with yourreasonaspayload.note, the acting user and organization, and — for a plot-grain finding — the plot astarget_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
| Status | Code | When |
|---|---|---|
| 400 | BAD_REQUEST | Unknown 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 |
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 403 | FORBIDDEN | Key lacks eudr:write |
| 404 | NOT_FOUND | Record is not visible to the caller |
| 409 | CONFLICT | The version is filed to TRACES and frozen |
Required scope:
eudr:write