Skip to main content

EUDR Compliance Use Case

Learn how to use Coolset APIs to meet EU Deforestation Regulation requirements.

Overview​

The EU Deforestation Regulation (EUDR) requires companies to prove that imported commodities don't contribute to deforestation. Coolset's EUDR API automates compliance workflows.

Compliance Workflow​

Step 1: Check the Risk Assessment​

Coolset runs EUDR risk assessments for your orders — they are not created through the API. Each order carries the identifier its assessments are filed under in pulse_params.identifier:

curl -X GET "https://developers-scranton.coolset.com/api/orders/123/?order_action=buying" \
-H "Authorization: ApiKey YOUR_API_TOKEN"
{
"id": 123,
"assessment_status": "passing",
"pulse_params": {
"identifier": "scranton$Order$123",
"model_identifier": "scranton$Order"
}
}

Look up the order's latest assessment by that identifier. There is no per-ID retrieve endpoint — filter the list instead:

curl -G https://developers-pulse.coolset.com/api/compliance/risk-assessments/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
--data-urlencode 'identifier=scranton$Order$123' \
--data-urlencode 'assessment_type=eudr_order_assessment' \
--data-urlencode 'latest_per_identifier=true'

Response:

{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 789,
"identifier": "scranton$Order$123",
"display_name": "PO-2024-001",
"assessment_type": "eudr_order_assessment",
"assessment_run_id": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"assessment_summary": {
"status": "passing"
},
"results": {},
"metadata": {},
"assessment_date": "2024-10-28T12:00:00Z"
}
]
}

Assessments run asynchronously, so a fresh one starts out pending with an empty results. Read assessment_summary.status:

StatusMeaning
pendingNothing has been evaluated yet
in_progressSome controls are still being evaluated
failingAt least one control failed — remediate before filing a DDS
mitigatedRisks were found and mitigated — a DDS can be filed
passingAll controls pass — a DDS can be filed
note

results, metadata and the rest of assessment_summary are free-form JSON whose shape depends on assessment_type and may change between assessment versions. Key your integration off assessment_summary.status and assessment_run_id, and treat the other fields as informational.

Keep the assessment_run_id — Step 2 pins the Due Diligence Statement to it.

Step 2: Create the Due Diligence Statement​

Once the risk assessment resolves to passing or mitigated, create a DDS and attach the assessed order(s) directly via orders:

curl -X POST https://developers-scranton.coolset.com/api/due-diligence-statements/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "EUDR",
"orders": [123, 456],
"assessment_run_ids": ["a1b2c3d4-e5f6-4789-a012-3456789abcde"]
}'
  • type — the regulation the statement is filed under (EUDR, EUTR, or PPWR).
  • orders / products — optional arrays of order/product IDs to attach at creation time; you don't need a separate call to link them.
  • assessment_run_ids — required for type=EUDR when you don't supply dds_identifier. It pins the statement to the risk assessment run(s) that justify it, and is immutable after creation.
  • dds_identifier — optional; if you already have a DDS filed with EU TRACES, pass its identifier here to retrieve/link it instead of filing a new one (in that case assessment_run_ids isn't required).
  • name — optional free-text label.

Prerequisite: the linked risk assessment(s) in assessment_run_ids must have resolved to passing or mitigated before a DDS can be created from them. Submitting against a pending assessment fails with a validation error, and a failing assessment must be remediated first — see Issue: High Risk Assessment below — before you retry.

Adding more orders to an existing DDS later (optional)​

If you need to attach additional orders to a DDS after it's already been created, add order traces:

curl -X POST https://developers-scranton.coolset.com/api/traces/orders/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dds_id": [789],
"order_id": [789, 1011]
}'

Both dds_id and order_id accept a single integer or an array, and the orders are added to the existing statement(s) — this is not a prerequisite for creating a DDS; use it only when you need to expand an already-filed statement.

Step 3: Upload Supporting Documents​

Add evidence to support your DDS:

# 1. Create the document record. `identifiers` links it to the order.
curl -X POST https://developers-pulse.coolset.com/api/documents/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Certificate of Origin.pdf",
"content_type": "application/pdf",
"identifiers": ["scranton$Order$123"]
}'
# Response: {"document_id": 5521, "upload_url": "https://storage.googleapis.com/...", ...}

# 2. Upload the file to `upload_url` within 15 minutes (max 100 MB).
curl -X PUT "UPLOAD_URL" \
-H "Content-Type: application/pdf" \
-H "x-goog-content-length-range: 0,104857600" \
--upload-file "Certificate of Origin.pdf"

# 3. Confirm the upload. 200 = done, 202 = not visible yet, retry shortly.
curl -X POST https://developers-pulse.coolset.com/api/documents/5521/confirm_upload/ \
-H "Authorization: ApiKey YOUR_API_TOKEN"

Step 4: Download Evidence Package​

See which folders and files the assessment's evidence bundle contains:

curl -X GET https://developers-pulse.coolset.com/api/compliance/risk-assessments/789/evidence-bundle/ \
-H "Authorization: ApiKey YOUR_API_TOKEN"
{
"groups": [
{
"assessment_run_id": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"identifier": "scranton$Order$123",
"assessment_type": "eudr_order_assessment",
"display_name": "PO-2024-001",
"items": [
{
"key": "<blob key>",
"name": "certificate-of-origin.pdf",
"size_bytes": 182311,
"content_type": "application/pdf",
"last_modified": "2024-10-28T12:00:00Z"
}
]
}
]
}

Then download the whole bundle as a ZIP — or only some files, by passing their keys as selected_items:

curl -X POST https://developers-pulse.coolset.com/api/compliance/risk-assessments/789/evidence-downloads/ \
-H "Authorization: ApiKey YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"selected_items": []}' \
--output evidence-package.zip

Complete Example: Python​

import time
import requests

PULSE_API_URL = "https://developers-pulse.coolset.com/api"
SCRANTON_API_URL = "https://developers-scranton.coolset.com/api"
HEADERS = {
"Authorization": "ApiKey YOUR_API_TOKEN",
"Content-Type": "application/json"
}

SUBMITTABLE = {"passing", "mitigated"}
UNRESOLVED = {"pending", "in_progress"}


def latest_order_assessment(identifier):
# No per-ID retrieve endpoint: filter the list by identifier.
page = requests.get(
f"{PULSE_API_URL}/compliance/risk-assessments/",
headers=HEADERS,
params={
"identifier": identifier,
"assessment_type": "eudr_order_assessment",
"latest_per_identifier": "true",
},
).json()
return page["results"][0] if page["results"] else None


def eudr_compliance_workflow(order_id):
order = requests.get(
f"{SCRANTON_API_URL}/orders/{order_id}/",
params={"order_action": "buying"},
headers=HEADERS,
).json()
identifier = order["pulse_params"]["identifier"]

# Step 1: wait for Coolset's assessment of the order to resolve
assessment = latest_order_assessment(identifier)
while assessment is None or assessment["assessment_summary"].get("status") in UNRESOLVED:
time.sleep(30) # use a real retry/backoff policy in production
assessment = latest_order_assessment(identifier)

status = assessment["assessment_summary"]["status"]
print(f"Assessment status: {status}")

# Step 2: only passing/mitigated assessments can back a DDS
if status in SUBMITTABLE:
dds = requests.post(
f"{SCRANTON_API_URL}/due-diligence-statements/",
headers=HEADERS,
json={
"type": "EUDR",
"orders": [order_id],
"assessment_run_ids": [assessment["assessment_run_id"]],
},
).json()

print(f"DDS Created: {dds['id']} (status: {dds['status']})")
return dds

# Failing assessments need remediation first
print("Assessment failing - gather evidence before filing")
return None


result = eudr_compliance_workflow(123)

Key Concepts​

Assessment Types​

EUDR assessment types you will see on assessment_type:

  • eudr_order_assessment - Assess an entire order (backs a DDS)
  • eudr_order_item_assessment - Assess individual order items
  • eudr_origin_assessment - Assess a geographic origin
  • eudr_origin_harvest_assessment - Assess an origin for a specific harvest
  • eudr_supply_chain_assessment - Assess the full supply chain
  • eudr_company_maturity_self_assessment - Supplier maturity self-assessment
  • eudr_due_diligence_statement_assessment - Assess a Due Diligence Statement

Required Information​

For EUDR compliance, you need:

✅ Commodity type and quantity
✅ Country of production
✅ Geolocation coordinates (min 4 decimal places)
✅ Harvest date
✅ Supplier information
✅ Certificate of origin

Best Practices​

1. Monitor Assessment Status​

Coolset does not send webhooks yet. Check assessments on a schedule, or whenever your own system changes an order, and route anything that isn't submittable to your compliance team:

def check_order(order):
assessment = latest_order_assessment(order['pulse_params']['identifier'])
status = assessment['assessment_summary'].get('status') if assessment else None

if status == 'failing':
send_alert_to_compliance_team(order, assessment)

2. Track Geolocation Accuracy​

Ensure coordinates meet EUDR requirements (4 decimal places = ~11m accuracy):

def validate_geolocation(lat, lon):
# Check decimal precision
lat_precision = len(str(lat).split('.')[-1])
lon_precision = len(str(lon).split('.')[-1])

return lat_precision >= 4 and lon_precision >= 4

3. Maintain Evidence Packages​

Download and archive evidence packages regularly:

import schedule

def archive_evidence_monthly():
assessments = get_monthly_assessments()

for assessment in assessments:
package = download_evidence_package(assessment['id'])
store_in_archive(package, assessment['identifier'])

# Run on first day of each month
schedule.every().month.at("00:00").do(archive_evidence_monthly)

Compliance Checklist​

Use this checklist to ensure EUDR compliance:

  • All orders have resolved risk assessments
  • Geographic origins are documented
  • Geolocations meet precision requirements
  • Harvest dates are recorded
  • Supplier due diligence completed
  • Supporting documents uploaded
  • DDS created for compliant orders
  • Evidence packages archived
  • Non-compliant orders flagged
  • Regular compliance audits scheduled

Common Issues​

Issue: Insufficient Geolocation Data​

Solution: Request precise coordinates from suppliers upfront

def validate_supplier_data(data):
required_fields = [
'geolocation.latitude',
'geolocation.longitude',
'harvest_date',
'certificate_url'
]

missing = [f for f in required_fields if not data.get(f)]

if missing:
send_data_request_to_supplier(missing)
return False

return True

Issue: High Risk Assessment​

Solution: Gather additional evidence; the assessment is re-evaluated once the new evidence is in Coolset

def handle_high_risk(assessment):
# Request additional documentation
documents_needed = [
'Certificate of Origin',
'Deforestation-Free Declaration',
'Satellite Imagery',
'Chain of Custody Documentation'
]

request_documents_from_supplier(documents_needed)

# Schedule manual review
create_compliance_task(assessment['id'], 'high_risk_review')

API Endpoints Used​

This use case demonstrates:

Next Steps​

Call +31 20 2101245 on FaceTime