Assessments on Ingestion Responses
โถIncluding assessments on ingestion responses
The fastest way to read a location's assessments alongside its ingestion record is the opt-in assessments include on the asset-results endpoint:
curl '/ingestions/assets/{asset_task_id}?include=assessments' \
-H 'x-api-key: ...'
{asset_task_id} is the id returned when you upload (POST /ingestions/assets) or process (PUT /ingestions/assets) a batch. The response is the normal paginated asset-results envelope; each asset gains a relationships.assessments object carrying every assessment partition for that location in one fetch โ no second round-trip per partition. This is usually the first integration point for a new consumer: ingest, then read the confidence score, hazard tier, distances, and EUDR risk levels off the same record.
For deeper queries against a single partition โ filtering by risk level, paginating one row per applicable law, or refreshing โ use the dedicated /assessments/{location_type}/{slug} endpoints. This include is the wide, one-row-per-location view; those slugs are the narrow, filterable, per-partition view. Both read the same underlying assessment rows.
The same aggregate is also available on the locations surface and on the DDS plot endpoints. All three surfaces share one schema, so a partition looks identical wherever you read it.
โถOn the locations surface
The locations endpoints carry the same aggregate under the opt-in assessment include โ note the singular assessment here (the ingestion surface uses the plural assessments; they address the same rows):
# Every location in a list
curl '/locations?include=assessment' -H 'x-api-key: ...'
# A single location (also works on the typed convenience routes)
curl '/locations/plots/{id}?include=assessment' -H 'x-api-key: ...'
curl '/locations/{id}?include=assessment' -H 'x-api-key: ...'
The include is valid on the list routes (GET /locations, /locations/facilities, /locations/sheds, /locations/plots, /locations/commodity/{commodity}) and the single route (GET /locations/{id}). Each location gains a relationships.assessment object with the identical partition shape documented below.
The base list/single response is gated on locations:read; the three EUDR-scope partitions (art9, art10, eudr_profile) are additionally gated on assessments:read, exactly as on the ingestion surface โ see EUDR-scope gate. Without include=assessment the response is unchanged (the relationship is omitted entirely). For deeper per-partition queries โ filtering by risk level, paginating one row per applicable law, or refreshing โ use the dedicated /assessments/{location_type}/{slug} endpoints.
โถResponse shape
relationships.assessments.attributes carries one slot per partition. Every slot is nullable โ a slot is null when the partition was never produced for that location, when your organisation can't see it, or (for the EUDR-scope partitions) when your key lacks the scope to read it (see EUDR-scope gate).
{
"data": [
{
"type": "asset",
"id": "โฆ",
"attributes": { "location_type": "plot", "status": "completed", โฆ },
"relationships": {
"assessments": {
"type": "assessments",
"attributes": {
"core": { "confidence_score": 0.91, "confidence_level": "high", "flags": { โฆ }, "sco2_run_at": "2026-01-12T08:04:11Z" },
"hazard": { "flags": { "natural_hazard_risk_level": "low", "flood_zone_pct": 4.2, โฆ }, "sub_options": null, "sco2_run_at": "โฆ" },
"infrastructure": { "distances": { โฆ }, "shed": { โฆ }, "climate": { โฆ }, "sub_options": { โฆ }, "sco2_run_at": "โฆ" },
"deforestation_screen": { "flags": { "deforestation": { โฆ }, "deforestation_check_failed": false }, "sub_options": null, "sco2_run_at": "โฆ" },
"art9": { "flags": { "legality_risk_level": "low", โฆ }, "sub_options": null, "sco2_run_at": "โฆ" },
"art10": { "flags": { "article10_risk_level": "low", โฆ }, "sub_options": null, "sco2_run_at": "โฆ" },
"eudr_profile": { "flags": { "eudr_country_risk_tier": "standard", โฆ }, "sub_options": null, "sco2_run_at": "โฆ" },
"legality_risk_level": "low",
"article10_risk_level": "low",
"failure_reason": null
}
}
}
}
],
"meta": { "asset_task_id": "โฆ", "overall_status": "completed", โฆ },
"links": { "self": "โฆ" }
}
โถThe partition envelope
Most partitions share one envelope shape:
| Key | Meaning |
|---|---|
flags | The partition's data keys (a strict, enumerated object โ unknown keys are dropped, never echoed). |
sub_options | Snapshot of the check_* sub-options active when the assessment was produced. Only infrastructure populates it; null everywhere else. |
sco2_run_at | ISO 8601 timestamp of when the row was last produced. |
Two partitions deviate:
corepromotes its headline fields to the top of the slot โconfidence_score,confidence_level,confidence_comment,producer_confirmation_score,producer_confirmed,producer_confirmation_commentโ with the remaining validation keys underflags. (confidence_score/confidence_levelcome from dedicated columns and are not duplicated inflags.)infrastructurereplaces theflagsgrab-bag with three named sub-objects โdistances,shed,climateโ sourced across partitions. See the Infrastructure section below.
โถPartitions at a glance
| Slot (response key) | Public slug | Scope | Contents |
|---|---|---|---|
core | core | ingestion | Confidence score/level + geometric & facility-plausibility flags. |
hazard | hazard | ingestion | Natural-hazard risk + physical-risk metrics (flood, heat, water stress, grid distance). |
infrastructure | infrastructure | ingestion | Nearest-feature distances (distances), supply-shed footprint (shed), climate overlay (climate). |
deforestation_screen | deforestation-screen | ingestion | Validation-time deforestation summary + deforestation_check_failed flag. |
art9 | legality | EUDR | Article 9 isochrone deforestation + legality columns. |
art10 | country-risk | EUDR | Article 10 procedural-risk scoring + corruption index. |
eudr_profile | eudr-profile | EUDR | Gemini facility profile + Article-29 country tier. |
legality_risk_level | โ | ingestion | Headline Article 9 level, promoted from art9 (always surfaced). |
article10_risk_level | โ | ingestion | Headline Article 10 level, promoted from art10 (always surfaced). |
failure_reason | โ | ingestion | Per-asset processing failure reason, or null. |
The response keys are the internal partition names (
art9,art10). The regulation-neutral public slugs (legality,country-risk,eudr-profile) are the URL segments on the/assessments/*endpoints. They address the same rows.
โถEUDR-scope gate
The base response is gated on ingestions:read (or locations:read on the locations surface). The three EUDR-scope partitions โ art9 (legality), art10 (country-risk), and eudr_profile โ are additionally gated on assessments:read:
- A key with
assessments:read(or an unrestricted key) sees these slots populated when the data exists. - A key without
assessments:readalways sees them asnullโ indistinguishable from "no such row." We don't reveal that gated data exists.
The two promoted scalars โ legality_risk_level and article10_risk_level โ stay ungated: they're always surfaced under the base scope so a list or table row carries the headline EUDR risk levels without a second fetch. The full detail (sources, applicable laws, scoring breakdown) stays on the gated art9 / art10 slots and the dedicated /assessments/{location_type}/legality and /country-risk slugs.
How the EUDR-scope partitions get populated varies: art9 / art10 only by a DDS create (POST /regulations/eudr/audit-trail); eudr_profile either by opting into include_eudr_profile on a plain ingestion (POST / PUT /ingestions/assets) or force-run at DDS creation. An ingestion that enables only include_hazard / include_infrastructure / include_core (i.e. without include_eudr_profile) leaves all three null regardless of scope. See assessments-overview.md for how each partition is produced.
โถField reference
The field-by-field reference for the ingestion-scope partitions lives in assessment-fields.md โ the keys are identical here, nested under each slot's flags (or, for core, split between the promoted top-level fields and flags):
coreโ Core grouphazardโ Hazard groupart9(legality) โ Article 9 groupart10(country-risk) โ Article 10 group
The two partitions split out after that page was written โ eudr_profile and deforestation_screen โ are documented here.
โถinfrastructure
infrastructureinfrastructure projects three named sub-objects rather than a flat flags object:
| Sub-object | Key fields |
|---|---|
distances | nearest_water_km, nearest_port_km, nearest_road_km, port_road_km, nearest_port_name, nearest_port_address, nearest_port_point, nearest_water_point, nearest_grid_km, nearest_grid_point. |
shed | shed_area_ha, commodity_area_ha, natural_forest_area_ha. |
climate | flood_zone_pct, flood_event_count, precip_trend_pct, water_stress_score, extreme_heat_wbgt. |
Which distance keys appear depends on the check_* flags set at request time; sub_options records that snapshot. Field descriptions: Infrastructure group.
โถeudr_profile flags (EUDR-scope)
eudr_profile flags (EUDR-scope)The opt-in EUDR enrichment split out of art10: the Gemini facility extractor outputs plus the deterministic Article-29 country classification.
| Field | Type | Description |
|---|---|---|
facility_description | string | Free-text facility profile description. |
facility_profile | object | string | Structured Gemini facility profile (upstream-owned, shape-variant). |
eudr_country_risk_tier | string | EUDR Article-29 country tier โ low | standard | high. |
eudr_article_29_citation | string | Citation for the Article-29 classification in force. |
eudr_article_29_effective_date | string | Effective date of that classification (YYYY-MM-DD). |
eudr_assessment_type | string | Derived assessment-type discriminator. |
โถdeforestation_screen flags (ingestion-scope)
deforestation_screen flags (ingestion-scope)The validation-time deforestation summary, promoted out of core into its own partition. Opt in with include_deforestation on either ingestion endpoint. The reprocess PUT also accepts include_deforestation_screen, which reaches the same sco2api primitive but runs the type as a standalone full assessment and requires the eudr:write scope; the upload POST rejects it.
| Field | Type | Description |
|---|---|---|
deforestation | object | sco2api deforestation summary โ { risk, deforestation_present, deforestation_area_ha } (upstream-owned, shape-variant). |
deforestation_check_failed | boolean | true only when the upstream deforestation check errored; false on the happy path. |
The summary's three fields come back null when the screen could not measure the location โ an unusable geometry, or an upstream reduce that errored. That is deliberately distinct from a measured zero (risk: "low", deforestation_present: false, deforestation_area_ha: 0), so read risk rather than treating a 0 area as a clean result.
The measurement differs by location type even though the shape does not. A facility's figure is deforestation over its supply-shed catchment (the isochrone, or a 50 km buffer when the isochrone solve fails). A plot's or shed's is deforestation over the uploaded polygon itself. The by_assessment progress for the group therefore folds from a different sco2api sub-task per type.
risk is a presence test, so do not compare the band across grains. It is high for any non-zero area and low for exactly zero โ the same rule at both grains, but over very different areas. A plot banded high has deforestation inside its own boundary. A facility banded high has an alert somewhere in its sourcing catchment, which can be ~785,000 ha when no isochrone was solved, over a roughly two-year alert window. Treat the facility band as a screening flag for follow-up, not as a finding about the site.
To rank facilities by severity, compute the annual rate of natural forest lost rather than reading the band: deforestation_area_ha รท natural_forest_area_ha, divided by the window ending at deforestation_monitoring_end_year (the EUDR cutoff is 31 Dec 2020). All three fields ride the art9 partition the same run writes.
Two things that rate deliberately is not:
- Not a cumulative share. Cumulative loss climbs on its own as the 2020 cutoff recedes, so two catchments measured in different years stop being comparable. Divide by the window.
- Not a share of
shed_area_ha. The loss is a strict subset of the natural-forest extent, so that extent is the denominator that means something; catchment area is context, not a divisor.
There is deliberately no finer band than low/high on either grain, because the thresholds have never been calibrated.
โถBooleans are JSON booleans
Presence and access flags (water_access, port_proximity, road_access, poi_presence, high_density on core.flags, and deforestation_check_failed on deforestation_screen.flags) are real JSON true / false, not the 0 / 1 integers sco2api emits upstream โ fm-api coerces them at ingestion. Tri-state gradients (commodity_presence, building_presence) stay numeric (0 | 0.5 | 1) on purpose.
core.flags.commodity_invalid is true on a row whose commodity cell names no supported commodity, and absent otherwise. See The commodity column.
โถBack-compatibility
Without include=assessments the response is unchanged: assets carry no assessments relationship. The include is purely additive and opt-in; existing integrations that don't request it see the same payload as before.