---
title: "Plot polygon → deforestation verdict + evidence CID."
method: POST
path: "/v1/spatial/verify"
tags: ["agent"]
---

# Plot polygon → deforestation verdict + evidence CID.

`POST /v1/spatial/verify`

## Request

- Base URL: `https://eudr.dev`
- URL: `https://eudr.dev/v1/spatial/verify`
- Auth: none declared

Hansen GFC + ESA WorldCover + JRC GFC2020 via emem.dev. Returns the verdict against the 2020-12-31 cut-off in Art. 2(13) Reg. (EU) 2023/1115.

## Request body

- SpatialVerifyBody: Supply either `polygon` (one plot) or `plots` (up to eight). Supplying neither is a 400 naming the omission.
  - `polygon` GeoJSONGeometry: GeoJSON Geometry — Polygon, MultiPolygon, or Point. WGS84 (EPSG:4326). Coordinates ordered [lng, lat].
    - `type` 'Polygon' | 'MultiPolygon' | 'Point', required
    - `coordinates` unknown, required
  - `plots` object[]: Several plots in one request, each keeping its own verdict row. Art. 4(1) taints the whole consignment if any single plot fails, so they belong in one call.
    - `plot_id` string: Echoed on the matching verdict row. [A-Za-z0-9-_.:]
    - `geojson` GeoJSONGeometry, required: GeoJSON Geometry — Polygon, MultiPolygon, or Point. WGS84 (EPSG:4326). Coordinates ordered [lng, lat].
      - `type` 'Polygon' | 'MultiPolygon' | 'Point', required
      - `coordinates` unknown, required
  - `commodity` 'cattle' | 'cocoa' | 'coffee' | 'oil_palm' | 'rubber' | 'soya' | 'wood', required
  - `country` string, required: ISO-3166-1 alpha-2.
  - `quantity_kg` number, nullable
  - `hs_code` string, nullable
  - `evidence_detail` 'cids' | 'anchor': `cids` embeds every per-cell fact CID; `anchor` returns only the address and count, resolvable at /v1/evidence/{evidence_cid}. The verdict is identical either way. On a real plot the array is the large majority of the body.

## Response `200`

Verdict + evidence

- SpatialVerifyResponse: The verdict, what was inspected to reach it, and what was NOT checked. Every response names the unresolved checks so a clean deforestation result is never read as a clean due-diligence file.
  - `deforestation_free` boolean, nullable
  - `post_cutoff_loss_cells` integer: Signed population tally of cells with post-cut-off canopy loss, not the shipped sample. This is loss OBSERVED, not deforestation found: see `in_scope_loss_cells`.
  - `in_scope_loss_cells` integer: Cells with post-cut-off loss on the plots that trip the Art. 2(13) gate — the loss the Regulation counts as deforestation. Below `post_cutoff_loss_cells` whenever the responder cleared a plot as non-forest at the cut-off, or under the 0.5 ha the Art. 2(4) forest definition requires. A row the responder cleared does not bar the consignment.
  - `sub_mmu_loss_ha` number: Hectares of sub-floor loss across the consignment, present only when they reach the floor together and that is why the verdict is indeterminate. One patch below 0.5 ha is not forest; a clearing split across twenty plots is.
  - `forest_baseline` string: The forest baseline the responder actually read, as it named it (e.g. `jrc_gfc2020_v4`).
  - `precision_warnings` object[]: The responder's warnings, verbatim, that a plot's coordinates carry fewer than the six decimal digits Art. 2(28) requires of a geolocation. Omitted when there are none.
  - `total_cells` integer
  - `verdict` 'negligible' | 'non_negligible' | 'not_in_scope' | 'indeterminate': Consignment verdict. `not_in_scope` here means the commodity is outside Annex I. A per-plot row's `not_in_scope` means something else: the land was not forest at the 2020 cut-off, so the plot is deforestation-free by definition, and the consignment verdict can still be `negligible`.
  - `risk_score` number, nullable
  - `country_tier` 'low' | 'standard' | 'high'
  - `cut_off_date` string
  - `per_plot` object[]: One row per submitted plot, in submission order. A single-plot request still gets one row.
    - `plot_id` string
    - `verdict` string
    - `cells_evaluated` integer
    - `cells_with_post_cutoff_loss` integer
    - `area_ha_approx` number, nullable
    - `loss_assessment` 'in_scope_deforestation' | 'not_forest_at_cut_off' | 'below_minimum_mapping_unit' | 'no_loss' | 'undecided': What the loss on this plot amounts to in law. A cell count alone does not say, and reading it as though it did barred consignments the same response cleared.
    - `loss_area_ha` number: Hectares of post-cut-off loss, as the responder measured it. Omitted when it did not report one.
    - `precision_warning` string: The responder's warning that this plot's coordinates are below the six decimal digits Art. 2(28) requires. Omitted when clean.
  - `coverage` object: What was actually inspected. A verdict over 2 % of a polygon and a verdict over all of it are different claims.
    - `cells_evaluated` integer
    - `cell_size_m` number
    - `sampled_area_ha` number, nullable
    - `plot_area_ha` number, nullable
    - `sampled_polygon_fraction` number, nullable: 1.0 is a full sweep.
    - `sampling_note` string, nullable: The upstream responder's own disclosure, verbatim.
    - `per_cell_rows_returned` integer: How many per-cell rows came back with the answer. `cells_evaluated` is the responder's signed population tally and is what every count here is on; these rows are the sample it shipped, and they are fewer.
  - `loss_years_observed` integer[]: Distinct Hansen loss years in the shipped sample, ascending. Dates what was found; never counts it.
  - `unresolved_checks` string[]: Checks this engine does not perform. Art. 3(b) legality is documentary and unobserved here.
  - `evidence` object: Note the nesting: `evidence_cid` lives HERE, not at the top level. An earlier revision of this document put it at the top level, which is a field the response has never carried.
    - `evidence_cid` string, nullable: Content address of THIS response's fact list. BLAKE3 over its canonical JSON, so a caller can re-derive it from the returned CIDs. It is not a fingerprint of the question: emem signs each fact with the moment it was signed, so re-running the same check returns the same measurements under new addresses. Resolve at /v1/evidence/{evidence_cid}.
    - `fact_count` integer: How many facts the check resolved. Always present.
    - `fact_cids` string[]: Omitted entirely when `evidence_detail: "anchor"` was sent.
    - `responder_pubkey_b32` string, nullable
    - `algorithm` string, nullable
  - `engine` string

## Other responses

- `400`: Validation error
- `429`: Rate limit (60 rpm sliding window per IP)

---

[API](https://skmtc.dev/eudr/apis/eudr-dev-eu-deforestation-regulation-compliance-api.md) · [All operations](https://skmtc.dev/eudr/apis/eudr-dev-eu-deforestation-regulation-compliance-api/llms.txt) · [OpenAPI document](https://skmtc.dev/eudr/apis/eudr-dev-eu-deforestation-regulation-compliance-api/revisions/ddffcc4295bf?raw)
