← Back to Dashboard

EUDR Enforcement Integration Guide

Everything an enforcement agency or platform partner needs to reproduce the EUDR Enforcement Dashboard using the Epoch SCO2 API.

Contents

  1. Overview & Architecture
  2. Authentication
  3. API Endpoints
  4. Dashboard Tabs
  5. Facility Confidence Scoring
  6. Risk Classification Logic
  7. Map Visualization
  8. Batch Processing Workflow
  9. Audit Trail (DDS) Export
  10. Parsing NDJSON Responses

1. Overview & Architecture

The Enforcement Dashboard provides two complementary workflows for EUDR compliance monitoring:

  1. DDS Submission Screening (default tab) — Upload GeoJSON, CSV, or XLSX files to screen plot or facility locations. Supports geocoding addresses directly, per-facility confidence scoring, and submission to batch processing.
  2. Country Commodity Assessment — Aggregate view of all assessed producers, grouped by country and commodity. Supports risk-tiered status (High Risk, Medium Risk, Compliant) based on the percentage of non-compliant area. Enables drill-down to individual plot-level results.

All data is fetched from the Epoch SCO2 REST API. The dashboard is a static HTML page — no backend required beyond the API.

2. Authentication

All API requests require a Bearer token. Obtain one by calling the /refresh_token endpoint with user credentials in a JSON body:

POST https://epoch-sco2-api.com/refresh_token
Content-Type: application/json

{ "email": "USER", "password": "PASS" }

Response:

{ "access_token": "eyJhbGciOiJSUzI1NiIs..." }

Include the token in all subsequent requests:

Authorization: Bearer <access_token>
Server-to-server alternative: For backend integrations, use x-internal-api-key and x-user-id headers instead of Bearer tokens. Contact Epoch to receive your API key.

Base URLs:

3. API Endpoints

GET /fetch_supply_shed

Returns all producers/facilities as streaming NDJSON. Each line is a GeoJSON Feature with compliance metrics and metadata. This is the primary endpoint for loading the Country Commodity Assessment tab.

Key Parameters

ParameterValuePurpose
stat_type"all"Return all supply sheds regardless of assessment type
table_extension"eudr"Query the EUDR-specific results table
limit100000Max rows (omit for all)

Example

GET /fetch_supply_shed?stat_type=all&table_extension=eudr&limit=100000

Key Response Fields (per feature)

FieldTypeDescription
collection_idstringUnique identifier for this assessment
company_namestringProducer/supplier name
commoditystringCommodity type (rubber, timber, palm_oil, etc.)
countrystringFacility country
admin1stringAdmin level 1 region (province/state)
locations_confidencestringhigh/medium/low = assessed; anything else = skip reason
noncompliance_plot_countintNumber of plots with critical deforestation alerts
noncompliance_area_hafloatTotal non-compliant area in hectares
noncompliance_area_percfloatNon-compliant area as fraction of total area (0.0 to 1.0). Used for risk classification.
noncompliance_area_ratefloatAnnualized non-compliance rate
area_ha_supply_shedfloatSupply shed area (> 0 = supply shed assessment)
stat_typestringAssessment type (contains supply_shed if supply shed)
legality_riskstringArticle 10 risk: Low / Medium / High
ingestion_datetimestampWhen the assessment was processed
metadataJSON stringCustom metadata (supplier IDs, part IDs, contact emails)
GET /fetch_deforestation_check

Returns plot-level deforestation results for a specific producer. Used for the drill-down detail view when a user clicks a row in the aggregate table.

Key Parameters

ParameterValuePurpose
aggregatefalseReturn individual plots (not aggregated)
limit100000Max plots
filenamee.g. "0x49a7..."Collection ID to fetch plots for

Example

GET /fetch_deforestation_check?aggregate=false&limit=100000&filename=0x49a7...

Key Response Fields (per plot)

FieldTypeDescription
uuidstringUnique plot identifier
areafloatPlot area in hectares
noncompliance_areafloatDeforested area in hectares
noncompliance_area_percfloatDeforestation percentage of plot area
deforestation_alertstring"critical", "non-critical", or "no deforestation"
deforestation_confidencestringDetection confidence (very high, high, medium, low, very low)
geometryGeoJSONPlot polygon
POST /validate_locations

Runs real-time location screening on uploaded GeoJSON, CSV, or XLSX files. Used by the DDS Screening and Producer Screening tabs. Supports both plot polygons and facility points. CSV/XLSX files are automatically geocoded via the Google Geocoding API.

Key Parameters

ParameterValuePurpose
fileGeoJSON / CSV / XLSXLocation geometries to screen (multipart/form-data)
location_type"plot" or "facility"Type of locations being screened
commodity_typee.g. "palm_oil"Expected commodity (used for commodity presence check). If omitted, auto-detect is attempted.
include_deforestationtrueInclude deforestation detection. Plots: per-plot 30 m check. Facilities: 50 km radius, natural forest only, post-2021.
include_article10_checktrueAI web search Article 10 risk analysis. Plots: 1 call for collection area. Facilities: per-facility with Google Places enrichment. Sources ranked by relevance & freshness.
check_road_accesstrue(Facility only) Check proximity to road networks. Default: true
check_water_accesstrue(Facility only) Check proximity to waterways
check_port_proximitytrue(Facility only) Check proximity to ports
normalize_inputtrue / falseWhether to normalize input geometries. Auto-detect: false for GeoJSON/JSON/SHP files, true for CSV/XLSX (which need geocoding)
production_volumee.g. 1000(Plot only) Declared annual production in tonnes/year. Used for circumvention detection — compares declared volume against expected yield for the total plot area
limit10000Max locations to process

Example (Plot Screening)

POST /validate_locations?location_type=plot&include_deforestation=true&commodity_type=palm_oil&limit=10000
Content-Type: multipart/form-data
Body: file=@plots.geojson

Example (Facility Screening with Geocoding)

POST /validate_locations?location_type=facility&commodity_type=timber&check_road_access=true&normalize_input=true
Content-Type: multipart/form-data
Body: file=@facilities.csv

Plot Screening Response

Returns a GeoJSON FeatureCollection with collection-level properties containing validation results:

FieldTypeDescription
overall.riskstringOverall risk level (high / medium / low)
total_plotsintNumber of plots screened
total_plot_area_hafloatTotal area in hectares
plot_validity.shape_validityobjectrisk, issues (count by type: self-intersections, slivers, etc.)
plot_validity.commodity_presenceobjectrisk, commodity_absence_count (plots not overlapping declared commodity)
plot_validity.protected_area_overlapobjectrisk, protected_area_overlap_count (plots overlapping WDPA areas)
deforestationobjectrisk, plots_with_deforestation, deforestation_area_ha, plot_deforestation_pct
circumventionobjectrisk, deviation_pct (spatial deviation from expected patterns)
cherry_pickingobjectrisk, deforestation_pct (comparison of inside-vs-outside deforestation rates)
overall.investigate_categoriesobjectMap of category name to plot count needing investigation

Each feature in the collection includes per-plot scores for shape validity, commodity, protected areas, and deforestation.

Facility Screening Response

Returns a GeoJSON FeatureCollection with per-facility confidence scoring. Each feature includes:

FieldTypeDescription
confidence_scorefloatPer-facility confidence score (0-1). Penalty-based: starts at 1.0 and deducts for missing attributes. See Scoring.
confidence_levelstringhigh (≥0.7) / medium (≥0.45) / low
commodity_typestringDetected or declared commodity type
commodity_presencefloatCommodity overlap score (0-1)
poi_presenceint1 if facility is near known POIs (mills, factories), 0 otherwise
building_presenceint1 if building footprints detected in 100m buffer, 0 otherwise
high_densityint1 if building density > 50% of buffer area (urban), 0 otherwise. Negative signal — penalizes score.
road_accessint1 if road access within 500m (default: checked)
water_accessint1 if waterway access within 1km (optional)
port_proximityint1 if port within 50km (optional)
producer_namestringExtracted producer/company name from input columns (if available)
producer_confirmedboolWhether producer confirmation score exceeds threshold
producer_confirmation_scorefloatProducer confirmation score (0-1)

Collection-level properties include aggregated counts:

FieldTypeDescription
total_facilitiesintNumber of facilities screened
confidence_scorefloatAverage confidence score (0-1)
confidence_levelobjectDistribution: { "high": N, "medium": N, "low": N }
commodity_presenceintFacilities near declared commodity
poi_presenceintFacilities near points of interest
high_densityintFacilities in high building-density areas
road_accessintFacilities with road access (if checked)
water_accessintFacilities with waterway access (if checked)
port_proximityintFacilities near ports (if checked)
POST /batch_supply_shed  |  /batch_process

Submit screened locations for full asynchronous processing. /batch_supply_shed is used for facility points (generates isochrone-based supply sheds), /batch_process is used for plot polygons (direct deforestation assessment).

Key Parameters (multipart/form-data)

ParameterValuePurpose
fileGeoJSONSelected features from screening results
collection_namestringCollection name (auto-suggested: {commodity}_{country}_{date})
crop_typee.g. "timber"Commodity type for the batch
stats"eudr_deforestation"Statistics to compute
validate_locations"false"Skip validation (already validated during screening)
force_reprocess"false"Whether to force reprocessing of existing collections
eudr_dds"true"(batch_supply_shed only) Generate DDS audit trail

Example (Facility Batch)

POST /batch_supply_shed
Content-Type: multipart/form-data
Body:
  file=@timber_Suriname_2026-03-11.geojson
  collection_name=timber_Suriname_2026-03-11
  crop_type=timber
  stats=eudr_deforestation
  validate_locations=false
  force_reprocess=false
  eudr_dds=true

Response

Returns a JSON object with the batch job status and collection ID. Processing runs asynchronously via Prefect flows.

GET /export_eudr_dds

Downloads the EUDR audit trail as a ZIP archive containing the full Due Diligence Statement with deforestation results, satellite references, and compliance determination per plot.

Example

GET /export_eudr_dds/?collections=0x49a7...

Response: Binary ZIP file. Trigger as a browser download.

Only available for assessed producers (locations_confidence = high/medium/low). Disable the download button for "Not Assessable" rows.

4. Dashboard Tabs

4.1 DDS Submission Screening (Default Tab)

Endpoint: POST /validate_locations

The default view after login. Upload GeoJSON, CSV, or XLSX files to screen locations. The tab supports two location types:

Input Options

Configuration

OptionTypeDefaultDescription
Location TypeselectplotPlots (polygons) or Facilities (points/addresses)
Commodity TypeselectAuto-detectcocoa, coffee, palm, rubber, soy, timber, cattle
Include deforestationcheckboxunchecked(Plot only) Run deforestation overlay
Production volumenumber(Plot only) For circumvention detection (t/yr)
Check road accesscheckboxchecked(Facility only) Check road network proximity
Check water accesscheckboxunchecked(Facility only) Check waterway proximity
Check port proximitycheckboxunchecked(Facility only) Check port proximity

Screening Results

After screening, the sidebar shows summary cards and the map displays screened locations. A data table below the map shows all facilities/plots with sortable columns.

Facility table columns:

ColumnSource
Facility IDfacility_id or id or name, fallback: 1-based index
Addressfacility_address or address, fallback: facility_name
Commoditycommodity_type
Countrycountry
Scoreconfidence_score (numeric, 0-1)
Levelconfidence_level (high/medium/low)
Comm. Presencecommodity_presence
POIpoi_presence
Buildingbuilding_presence
Roadroad_access
Waterwater_access
Portport_proximity
High Densityhigh_density
Producer Nameproducer_name
Confirmedproducer_confirmed
Warningprimary_warning

Batch Submission

After screening, select facilities/plots via checkboxes and submit to batch processing. The collection name auto-suggests as {commodity}_{country}_{date} and updates on re-screening. Facilities go to /batch_supply_shed, plots go to /batch_process.

4.2 Country Commodity Assessment

Endpoint: GET /fetch_supply_shed?stat_type=all&table_extension=eudr&limit=100000

Loads all producer assessments and displays them in a sortable table with risk-tiered compliance status.

Two sub-views:

Columns:

ColumnSource
Producercompany_name
Commoditycommodity
Admin 1admin1
Countrycountry or country_code
AssessmentDerived: "Supply Shed" or "Direct Plots" (see risk logic)
StatusDerived: High Risk / Medium Risk / Compliant / Not Assessable
NC Plotsnoncompliance_plot_count
NC %noncompliance_area_perc * 100 (display as percentage)
Article 10legality_risk
Dateingestion_date
Audit"Download DDS" button

Summary cards: Total Producers, Compliant, High Risk, Medium Risk (≤5%), Not Assessable.

4.3 Plot Detail View (Drill-Down)

Triggered when a user clicks a producer row. Fetches plot-level data via:

GET /fetch_deforestation_check?aggregate=false&limit=100000&filename={collection_id}

Shows individual plot polygons on the map with binary compliance status (Compliant / Non-Compliant based on >0.5 ha noncompliance area). Summary cards show Total Plots, Compliant, and Non-Compliant counts.

5. Facility Confidence Scoring

Facility confidence uses a penalty-based model: each facility starts at 1.0 and has points deducted for missing or negative attributes.

Penalty Breakdown

CheckConditionPenalty
Commodity Presencecommodity_presence == 0-0.50
Commodity Presence (low)0 < commodity_presence < 1.0-0.25
POI Presencepoi_presence == 0-0.20
Building Presencebuilding_presence == 0-0.15
Road Accessroad_access == 0-0.15
High Density (urban)high_density == 1-0.10
Note: water_access and port_proximity are informational only — they do not affect the confidence score.

Confidence Levels

LevelScore RangeColor
High≥ 0.70Green (#16a34a)
Medium≥ 0.45Yellow (#ca8a04)
Low< 0.45Red (#dc2626)

Building Density Threshold

A facility is classified as high_density (urban) when building footprint area exceeds 50% of the 100m buffer area around the facility point. This is a negative signal — commodity processing facilities are typically in rural or peri-urban areas, not dense urban centers.

Facility Tooltip

On hover, the map tooltip shows the full scoring breakdown including each attribute's value and its penalty contribution. high_density = Yes should display in red (negative signal), while high_density = No displays in green (positive — rural area).

Producer Name Extraction

The API extracts producer_name from input CSV/XLSX columns by scanning for columns matching keywords: "name", "company", "producer", "facility". Columns that are part of the API's own output (facility_address, facility_id, commodity_type, scoring fields, etc.) are excluded from the scan to prevent false matches (e.g. address leaking into producer name).

6. Risk Classification Logic

Risk status is derived client-side from the API response fields. The key field is noncompliance_area_perc — the fraction of total assessed area flagged as non-compliant.

Producer-Level Risk (Aggregate)

StatusConditionColorBadge CSS Class
High Risknoncompliance_area_perc > 0.05 (more than 5% of area) OR noncompliance_plot_count > 100Red (#dc2626)high-risk
Medium Risknoncompliance_area_perc > 0 or noncompliance_plot_count > 0 (any non-compliance, but below High Risk thresholds)Yellow (#ca8a04)medium-risk
CompliantNo non-compliance detectedGreen (#16a34a)compliant
Not Assessablelocations_confidence not in (high, medium, low) and no noncompliance dataGray (#475569)unknown
function getComplianceStatus(p) {
  const canDo = isAssessable(p.locations_confidence)
    || p.noncompliance_plot_count != null
    || p.noncompliance_area_ha != null;
  if (!canDo) return { label: 'Not Assessable', cls: 'unknown' };

  const ncPerc = Number(p.noncompliance_area_perc || 0);
  const ncPlots = Number(p.noncompliance_plot_count || 0);
  const ncArea = Number(p.noncompliance_area_ha || 0);

  if (ncPerc > 0.05 || ncPlots > 100) return { label: 'High Risk', cls: 'high-risk' };
  if (ncPerc > 0 || ncPlots > 0 || ncArea > 0)
    return { label: 'Medium Risk', cls: 'medium-risk' };
  return { label: 'Compliant', cls: 'compliant' };
}

Plot-Level Compliance (Detail View)

Plot-level compliance uses a simple binary classification based on the noncompliance_area field (deforested area in hectares). A plot is non-compliant if more than 0.5 ha of deforestation is detected.

StatusConditionColor
Non-Compliantnoncompliance_area > 0.5 (ha)Red (#dc2626)
Compliantnoncompliance_area ≤ 0.5 (ha)Green (#16a34a)
// Binary compliance for individual plots
const ncArea = Number(p.noncompliance_area || 0);
const isNonCompliant = ncArea > 0.5;
const status = isNonCompliant ? 'Non-Compliant' : 'Compliant';
Note: Risk categorization (High Risk / Medium Risk) is only used at the aggregate producer level (from /fetch_supply_shed). The plot detail view from /fetch_deforestation_check uses binary compliant/non-compliant.

Assessment Type

if (area_ha_supply_shed > 0 && stat_type.includes('supply_shed')):
    type = "Supply Shed"    // isochrone-based area assessment
else:
    type = "Direct Plots"   // specific plot polygons provided

Assessability Check

function isAssessable(confidence) {
  return ['high', 'medium', 'low'].includes(confidence?.toLowerCase());
}

// A producer can be assessed if confidence is valid OR noncompliance data exists
function canAssess(p) {
  return isAssessable(p.locations_confidence)
    || p.noncompliance_plot_count != null
    || p.noncompliance_area_ha != null;
}

7. Map Visualization

The POC uses deck.gl with MapLibre GL. Any map library works.

Aggregate View (Country Commodity Assessment)

Facility points colored by risk status. Sort features so higher-risk points render on top (last in the array):

const riskOrder = { 'compliant': 0, 'unknown': 1, 'medium-risk': 2, 'high-risk': 3 };
pointFeatures.sort((a, b) =>
  (riskOrder[getComplianceStatus(a.properties).cls] || 0) -
  (riskOrder[getComplianceStatus(b.properties).cls] || 0)
);

Fill colors:

Point coordinates come from the feature's GeoJSON geometry, or parse facility_geo from metadata (WKT point) for facilities without geometry.

Screening View (DDS Submission Screening)

Facility points colored by confidence level:

Plot polygons colored by per-plot risk score or filtered by metric (overall risk, deforestation, protected area overlap).

Detail View (Plot Polygons)

Plot polygons from /fetch_deforestation_check, colored by binary compliance (>0.5 ha noncompliance area):

Overlay the supply shed boundary (blue outline) from the parent feature's geometry, and mark the facility location (yellow dot) if available.

8. Batch Processing Workflow

The screening-to-processing workflow in the dashboard:

  1. Screen — Upload file and run /validate_locations to get confidence scores and validation results.
  2. Review — Inspect results in the table and map. Select facilities/plots to submit via checkboxes.
  3. Submit — Click "Process Supply Shed" (facilities) or "Process Plots" (plots). This sends selected features to /batch_supply_shed or /batch_process respectively.
  4. Monitor — Processing runs asynchronously via Prefect flows. Results appear in the Country Commodity Assessment tab once complete.

Collection Naming

The collection name auto-suggests as {commodity}_{country}_{date} (e.g. timber_Suriname_2026-03-11) based on the first feature's properties. It updates automatically on re-screening.

Batch Parameters

9. Audit Trail (DDS) Export

Endpoint: GET /export_eudr_dds/?collections={collection_id}

Provide a "Download DDS" button for each assessed producer. Trigger a binary ZIP download. Disable the button for "Not Assessable" rows.

The ZIP contains the full Due Diligence Statement including:

10. Parsing NDJSON Responses

Both /fetch_supply_shed and /fetch_deforestation_check return Newline-Delimited JSON (NDJSON). Each line is a complete JSON object (GeoJSON Feature).

JavaScript Example (Streaming)

async function fetchNDJSON(url, token) {
  const resp = await fetch(url, {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  const features = [];
  const reader = resp.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    buffer = lines.pop(); // keep incomplete line in buffer
    for (const line of lines) {
      const trimmed = line.trim();
      if (!trimmed) continue;
      try {
        const obj = JSON.parse(trimmed);
        if (obj.type === 'Feature') features.push(obj);
        else if (obj.type === 'FeatureCollection') features.push(...(obj.features || []));
      } catch (_) {}
    }
  }
  // Process remaining buffer
  if (buffer.trim()) {
    try { const obj = JSON.parse(buffer.trim()); features.push(obj); } catch (_) {}
  }
  return features;
}

Python Example

import requests, json

def fetch_ndjson(url, token):
    resp = requests.get(url, headers={"Authorization": f"Bearer {token}"}, stream=True)
    features = []
    for line in resp.iter_lines(decode_unicode=True):
        if line.strip():
            features.append(json.loads(line))
    return features
For large datasets (100k+ features), use a streaming parser to avoid buffering the entire response in memory.

Quick Start Checklist

  1. Authenticate via /refresh_token — store the Bearer token
  2. Screen locations: Upload a file to /validate_locations with location_type=facility or plot
  3. Review facility confidence scores and plot validation results
  4. Submit selected locations to /batch_supply_shed (facilities) or /batch_process (plots)
  5. Monitor results: Fetch all producers via /fetch_supply_shed?stat_type=all&table_extension=eudr&limit=100000
  6. Derive risk status client-side using noncompliance_area_perc (see Section 6)
  7. Sort map features by risk tier so red points render on top
  8. Display summary cards: Compliant, High Risk, Medium Risk (≤5%), Not Assessable
  9. On row click: fetch plots via /fetch_deforestation_check?aggregate=false&limit=100000&filename={collection_id}
  10. Color plot polygons by binary compliance: red if noncompliance_area > 0.5 ha, green otherwise
  11. Download audit trail via /export_eudr_dds/?collections={collection_id}