Our API supports your journey to compliance in five key stages - Implementation, Supplier Engagement, Risk Assessment, Risk Mitigation, and Due Diligence & Record Keeping
Built around key EUDR requirements, Prewave APIs help you manage the essential steps efficiently. Once you've identified the relevant products under the EUDR framework, it’s important to ensure that their associated suppliers are already available in the Prewave system before uploading your product list. Our APIs are designed to guide you through this process smoothly, covering all core compliance scenarios.
Getting started and setting up your API
To get started with the API, you’ll first need an API token to access it. Please reach out to your dedicated Customer Success Manager, who will provide you with the token.
EUDR Endpoints
Below, we've listed all the EUDR-specific endpoints available in the API, which allows you to manage and retrieve EUDR information. For more detailed information you can access our API documentation.
Type | Action |
PUT | |
PUT | |
PUT | |
PUT | |
GET | |
POST | |
GET | |
POST | |
GET | |
POST | |
POST | |
GET | |
POST | |
POST | |
GET | |
GET | |
GET | |
GET | |
GET | |
GET | |
GET | |
DELETE | |
DELETE | |
DELETE |
Here are some API tutorials to help you explore and test the different functionalities available.
Products
Inbound Products
To enable product onboarding via the API, suppliers must first be set up in the system. The product reference serves as a unique identifier to link the products in your ERP system with those in Prewave.
If you wish to update product information, such as the annual quantity, you can do so by modifying the inbound product data.
Create Inbound Product
Create Inbound Product
Product Master Data
# Data needed to create a product, available in customer ERP system productName = "A Test Integration Product" productReference = "TIP111222" hsCode = "1801" annualQuantity = 1000 annualSpend = 20000 supplierName = "Fantasy Department For Testing 5" |
The commodity ID as well as the Supplier Reference Number are necessary to create a product.
# Get internal supplier ID url = f"{base_url}/public/v2/eudr/suppliers?query={supplierName}" response = requests.request("GET", url, headers=headers) supplierId = response.json()[0]['id'] print(supplierId) |
# Alternatively supplier name to supplier ID mapping stored in customer ERP suppliers = {"Fantasy Department For Testing 5":13756990} |
# HS codes to internal commodity ID mapping url = f"{base_url}/public/v2/eudr/hscodes" response = requests.request("GET", url, headers=headers) data = response.json() code_to_commodity = {item['code']: item['commodityId'] for item in data} |
# Create one inbound product url = f"{base_url}/public/v2/eudr/products/inbound" commodityId = code_to_commodity[hsCode] payload = json.dumps({ "name": productName, "scientificName": "", "reference": productReference, "commodityId": commodityId, "hsCode": hsCode, "annualSpend": annualSpend, "annualQuantity": annualQuantity, "unit": "kg", "supplierId": supplierId }) response = requests.request("POST", url, headers=headers, data=payload) productId = int(response.text) print(f"New product created, ID: {productId}") |
Update Inbound Product
Update Inbound Product
Set a new value to update a quantity of an inbound product
newAnnualQuantity = 5500 |
Ready to send the update product request
# Update one inbound product url = f"{base_url}/public/v2/eudr/products/inbound/{productId}" payload = json.dumps({ "name": productName, "scientificName": "", "reference": newProductReference, "commodityId": commodityId, "hsCode": hsCode, "annualSpend": 1000, "annualQuantity": newAnnualQuantity, "unit": "kg", "supplierId": supplierId }) response = requests.request("PUT", url, headers=headers, data=payload) print(response) |
Get Product
Get Product
To check the current status of origin requests and verify whether the conditions for DDS are met, you can use this endpoint. Additionally, it provides access to the deforestation check results, the Supplier Maturity Assessment outcomes, and the legality assessment score.
url = f"{base_url}/public/v2/eudr/products/inbound?scope=IDS&ids={productId}&includeOrigins=false" response = requests.request("GET", url, headers=headers) print(json.dumps(response.json()['content'], indent=4)) |
Outbound Products
To create a new outbound product, you’ll need to provide the supplier reference ID and the commodity ID. Additionally, outbound products must be linked to the relevant inbound products to ensure compliance.
Outbound Products
Outbound Products
Product Master Data
# Data needed to create a product, available in customer ERP system productName = "A Integration Outbound Product" productReference = "TIOP333444" hsCode = "1806" annualQuantity = 6000 annualRevenue = 45000 |
Supplier ID and Commodity ID are needed to create a product
# HS codes to internal commodity ID mapping url = f"{base_url}/public/v2/eudr/hscodes" response = requests.request("GET", url, headers=headers) data = response.json() code_to_commodity = {item['code']: item['commodityId'] for item in data} |
Create a new Outbound Product
url = f"{base_url}/public/v2/eudr/products/outbound" commodityId = code_to_commodity[hsCode] payload = json.dumps({ "name": productName, "scientificName": "", "reference": productReference, "commodityId": commodityId, "hsCode": hsCode, "annualRevenue": annualRevenue, "annualQuantity": annualQuantity, "unit": "kg" }) response = requests.request("POST", url, headers=headers, data=payload) productId = int(response.text) print(f"New outbound product created, ID: {productId}") |
List of Inbound Product References linked to the Outbound Product
inboundProductReferences = ["050405", "515566", "524962"] |
Filter by product reference to get internal inbound product ids using query parameter
inboundProductIds =[] for inboundProductRef in inboundProductReferences: url = f"{base_url}/public/v2/eudr/products/inbound?q={inboundProductRef}&includeOrigins=false&page=0&size=10" response = requests.request("GET", url, headers=headers) for product in response.json()['content']: inboundProductIds.append(product['id']) print(inboundProductIds) |
Link inbound products to outbound product
url = f"{base_url}/public/v2/eudr/products/outbound/{productId}/inbound" payload = json.dumps({ "inboundProductIds": inboundProductIds }) response = requests.request("POST", url, headers=headers, data=payload) print(response) |
Unlink inbound from outbound
unlinkProductId = inboundProductIds[0] url = f"{base_url}/public/v2/eudr/products/outbound/{productId}/inbound/{unlinkProductId}" payload = json.dumps({ "inboundProductIds": inboundProductIds }) response = requests.request("DELETE", url, headers=headers, data=payload) print(response) |
Filter Products
You can filter products based on various criteria, such as DDS status or inbound products with non-negligible deforestation check results. It is useful to create certain reportings.
Examples
Examples
# Filter inbound products with non-negligible deforestation checks and get their origins filter = "dfs=NonNegligible" url = f"{base_url}/public/v2/eudr/products/inbound?{filter}&includeOrigins=false&page=0&size=10" response = requests.request("GET", url, headers=headers) print(json.dumps(response.json()['content'], indent=4)) |
# Filter products with blocked DDS filter = "dd=Blocked" url = f"{base_url}/public/v2/eudr/products/inbound?{filter}&includeOrigins=false&page=0&size=10" response = requests.request("GET", url, headers=headers) print(json.dumps(response.json()['content'], indent=4)) |
# Filter by product name, supplier or product reference using query parameter filter = "q=Cocoa" url = f"{base_url}/public/v2/eudr/products/inbound?{filter}&includeOrigins=false&page=0&size=10" response = requests.request("GET", url, headers=headers) print(json.dumps(response.json()['content'], indent=4)) |
Get All Product Data
To retrieve all product data, you can use this endpoint. Please note that the response is paginated and may take several minutes to load, depending on the total number of products.
Get All Product Data
Get All Product Data
Get all products, filter our origins for faster response.
urls = [ f"{base_url}/public/v2/eudr/products/inbound?filter=scope%3DNAME&includeOrigins=false", f"{base_url}/public/v2/eudr/products/outbound?filter=scope%3DNAME&includeOrigins=false" ] data = [] for url in urls: response = requests.get(url, headers=headers) for page in range(response.json()["totalPages"]+1): response = requests.get(url+f"&page={page}", headers=headers) # Check if the response is successful if response.status_code == 200: json_data = response.json()["content"] # Ensure that the response is a list if isinstance(json_data, list): data.extend(json_data) else: print(f"Unexpected data format from {url}: Expected a list.") else: print(f"Failed to fetch data from {url}. Status code: {response.status_code}") # 'data' as a flat list containing all JSON objects from both responses print(f"{len(data)} products") |
Origins
To send out product origin requests, connection contacts must first be added. Initially, these contacts can be uploaded using an Excel file. Later, these can be managed and added through the Site Upsert API.
Origins
Origins
Add supplier connection contacts
url = f"{base_url}/public/v2/eudr/suppliers/{supplierId}/connection-contacts" response = requests.request("GET", url, headers=headers) connectionContactId = response.json()[1]['id'] print(response.text) |
Send out origin requests to your supplier connection contacts
url = f"{base_url}/public/v2/eudr/origin-requests" payload = json.dumps({ "items": [ { "productId": productId, "supplierId": supplierId, "connectionContactIds": [ connectionContactId ], "comment": f"Product origins requested for {productName}, HS code : {hsCode}", "sendCopy": False, "ccEmails": [ "string" ], "references": [ { "productId": productId, "reference": "PO9876", "referenceType": "PurchaseOrder" } ] } ] }) response = requests.request("POST", url, headers=headers, data=payload) print(response.text) |
Due Diligence Statements
Submit DDS
Submit DDS
If you wish to submit a Due Diligence Statement (DDS) within TRACES NT or use the TRACES Emulator for testing purposes in the sandbox environment, you can use this endpoint. Please note that submitting a DDS in the production environment is legally binding.
url = f"{base_url}/public/v2/eudr/customer-dds/{ddsId}/submission" response = requests.request("PUT", url, headers=headers) print(response.text) |
Fetch DDS
Fetch DDS
This endpoint retrieves the latest active Due Diligence Statement (DDS), including the reference and verification numbers if it has been submitted to TRACES. It can be used for purposes such as invoices, shipments, POs, and customs. Additionally, it allows you to verify the validity and check the expiration date of the DDS.
Get a customer DDS for one product
url = f"{base_url}/public/v2/eudr/products/{productId}/customer-dds" response = requests.request("GET", url, headers=headers) print(json.dumps(response.json(), indent=4)) |
DDS Status Reporting
DDS Status Reporting
To get an overview of the DDS status, you can generate a report. The results will be displayed in a table, including a calculated EUDR compliance score. Additionally, a high-level chart is created to visualize your overall compliance status.
Report Product DDS Status
import pandas as pd product_data = [] for item in data: try: product_name = item.get('name', 'N/A') supplier_name = item.get('supplier', {}).get('name', 'N/A') dds_status = item.get('ddsStatus', {}).get('status', 'N/A') dds_status_reason = item.get('ddsStatus', {}).get('reason', 'N/A') product_data.append({ 'Product Name': product_name, 'Supplier Name': supplier_name, 'DDS Status': dds_status, 'DDS Status Reason': dds_status_reason }) except AttributeError: # Handle cases where 'supplier' might not be a dictionary print(f"Skipping an item due to a missing or invalid 'supplier' field") continue # Create a Pandas DataFrame from the extracted product data df = pd.DataFrame(product_data) df |
High level EUDR compliance stats
complaint_count = df[df['DDS Status'].isin(['Allowed', 'Available', 'Draft'])].shape[0] noncompliant_count = df[df['DDS Status'].isin(['Blocked', 'WaitingForOrigins'])].shape[0] compliance_percentage = (complaint_count / (complaint_count + noncompliant_count)) * 100 print(f"Compliant products: {complaint_count}") print(f"Non-compliant products: {noncompliant_count}") print(f"Product compliance score: {compliance_percentage:.0f}%") |
Product DDS status stats
grouped_df = df.groupby('DDS Status') grouped_df.size() |
API Troubleshooting
Error Code | Response Message | Fix |
400 | "code": "countryID" | Required property value is missing. Provide valid JSONs |
401 | "code": "session_expired" | Check if your API key is valid, regenerate if needed. |
403 | "code": "access_denied" | Verify API key and request headers. |
404 | "code": "resource_not_found" | Ensure the endpoint URL is correct. |
500 | "code": "server_error" | Retry after refreshing or clearing cookies. |
400 | "code": "foreign_key_violation" | It is related to the connection between the commodity ID and the HS code. HS Code and commodity have to be of the same type (e.g. Wood related products need a wood HS code). |