Verdict overrides
One endpoint set for every kind of operator finding on a DDS record. A finding is a written, attributed statement an operator makes about what Epoch computed — either to overturn it, or to justify it without changing it.
| Endpoint | Purpose |
|---|---|
Set Verdict Override | Create or replace the finding on one (axis, subject) |
Remove Verdict Override | Delete that finding |
List Verdict Overrides | Read every finding on the record's current version |
Batch Verdict Override | Flip every subject on one axis in one call |
These replaced PUT/DELETE /:recordId/plots/:plotId/justification,
which were removed in EPO-1983. That pair was the plot-compliance axis with
no basis, no document and no validity window.
Migrating from the removed pair. It is not a path swap. The old PUT preserved an omitted
compliance_overrideand an omittedevidence_urls; this one is a full replace, so an omitted field becomesnull/[]. Send every field you want to keep. The response changed too: the old endpoints returned an operation envelope with anactionand a previous/next delta, while this one returns the finding row keyedaxis:subject_type:subject_id. Transition history now lives in the mitigation event log.Bulk
PATCH /configstill acceptsset_justifications/remove_justificationsand is unchanged.
▶The three axes
An axis is one dimension of the compliance decision. Each has its own grain, its own effect on submit, and its own accepted evidence.
| Axis | Grain | subject_id is | Effect on submit | Accepts documents of category |
|---|---|---|---|---|
plot_compliance | plot | a plot id | hard — an override to compliant clears the plots_non_compliant blocker | supporting_document, certificate |
legality | record | the record id | soft — warns, never blocks | legality_evidence, certificate |
exclusion | plot | a plot id | hard — the plot leaves the filing entirely | supporting_document, certificate, due_diligence_statement |
The exclusion axis removes the plot from the filing. Writing an
exclusionfinding is what takes a plot out of the DDS: it disappears fromGET /plots, appears onGET /plots/excluded, drops out of everyreport.geojson?include=mode, and is never sent to TRACES. It is still listed in the audit-trail PDF with its attestation, so the filing records what was left out and why.Deleting the finding is the only way back in. This replaced
POST .../plots/:plotId/excludeand.../include, which were removed along with the table behind them — the samePUTthat previously only annotated now performs the exclusion.
subject_type defaults to plot. For a legality finding you must send
subject_type: "record" and the record's own id as subject_id.
Every subject is validated against something you own, and a mismatch is a
400:
- a
plotsubject must be a plot in the record's current version; - a
recordsubject must be this record — naming another one is refused, because the report looks findings up by the record's own id and would never render it; entityis in the vocabulary but no axis resolves it, so it is refused outright rather than storing an id nothing can check.
▶Override versus attestation
Both are findings. The difference is one field.
- Override — send
verdict_override. The plot or record now reads ascompliant/non_compliantwhatever the satellite signal says. - Attestation — omit
verdict_override(or sendnull). The computed verdict stands; you have only put a reason on the record.
This distinction is load-bearing. An exclusion finding never
carries a verdict, because a plot outside the supply base is not
being declared compliant — it is being declared out of scope. And
a legality finding with no verdict_override leaves the
computed legality verdict in force; the audit trail will not claim
legality was cleared when nobody cleared it.
▶The computed signal always survives
An override never replaces what Epoch found. The report and the
PDF carry both, side by side, so a reviewer can weigh the
attestation against the evidence that triggered it. A plot flipped
to compliant still shows its deforested hectares.
▶Validity windows
valid_from / valid_to bound how long a finding applies. Both
are optional; omit them for a finding that does not expire.
The exclusion axis refuses them with a 400. An exclusion is a scope
decision, not time-bounded evidence, so letting one expire would pull a plot
back into a filing with nobody acting and no audit event. Delete the finding to
re-include the plot.
This exists because FLEGT and SVLK certificates are issued per
shipment. A clearance that was true in March must not silently
carry into July. Once the clock passes valid_to the finding
stops counting with no further action from anyone: the plot
reverts to its computed verdict, drops out of
?include=compliant, and trips the submit gate again.
Expired findings are retired, not deleted. They stay in
List Verdict Overrides and stay printed in the audit trail,
marked expired — the evidence of what was attested, and when,
remains part of the record.
▶Document-backed findings
Send document_attachment_id and the finding's provenance
becomes document instead of manual; the audit trail then
prints the document's filename beside the attestation. Upload the
file through the Attachments endpoints first.
The attachment is checked three ways, and any failure is a 400:
- it exists;
- it belongs to your organization — you cannot cite another operator's document, which matters because the audit trail prints its filename;
- it is not archived — a withdrawn document is not live evidence;
- its category is one this axis accepts (table above).
▶Freeze on submit
Once a version is filed to TRACES, its findings are immutable.
Every write on this folder answers 409 CONFLICT. Cut a new
version to make further changes; findings carry forward
automatically, with plot ids remapped if a refresh changed them.
▶Audit trail
Every write appends config.finding_set; every delete appends
config.finding_removed. Both carry the reason you supplied as
payload.note with payload.source = "user", the acting user and
organization, and — for a plot-grain finding — the plot as
target_plot_id. A rejected write appends nothing.
A batch write appends one config.finding_batch_set naming every subject it
moved, whatever the plot count.
Required scope:
eudr:writeto set or remove,eudr:readto list.