This guide walks you through the complete EUDR compliance lifecycle from first API call to a filed Due Diligence Statement. By the end you will have submitted farm polygons for satellite verification, registered a consolidated commodity batch on the Hedera ledger, linked it to a Bill of Lading, and generated a TRACES NT–compliant DDS — all programmatically.
Run through this quickstart against the sandbox environment first. Use an API key prefixed with sk_test_ so no real Hedera transactions or TRACES NT submissions occur.
Prerequisites
An AgriBackup account with at least one API key. Generate keys from the developer console or via POST /api/v1/enterprise/api-keys.
One of the three supported runtimes: Python 3.9+, Node.js 18+, or Java 17+.
Step 1 — Install the SDK
pip install agribackup-sdk
npm install @agribackup/sdk
Add the dependency to your pom.xml: < dependency >
< groupId > com.agribackup </ groupId >
< artifactId > agribackup-sdk </ artifactId >
< version > 1.0.0 </ version >
</ dependency >
Step 2 — Authenticate
Pass your API key once during client initialization. The SDK reads the sk_live_ or sk_test_ prefix and routes all requests to the correct base URL automatically — you never need to hard-code a host.
import agribackup
configuration = agribackup.Configuration()
configuration.api_key[ 'ApiKeyAuth' ] = 'sk_test_YOUR_API_KEY'
import { AgriBackupClient } from '@agribackup/sdk' ;
const client = new AgriBackupClient ({
apiKey: 'sk_test_YOUR_API_KEY'
});
import com.agribackup.client. * ;
ApiClient defaultClient = Configuration . getDefaultApiClient ();
defaultClient . setApiKey ( "sk_test_YOUR_API_KEY" );
Replace sk_test_YOUR_API_KEY with the key you generated from the dashboard.
Step 3 — Ingest farm polygons
Submit GeoJSON farm boundaries for satellite deforestation screening. This endpoint is asynchronous — it returns 202 Accepted with a jobId immediately. The actual Copernicus screening runs in the background; you receive a polygon.verified webhook (or can poll the job endpoint) when screening completes.
import agribackup
from agribackup.rest import ApiException
configuration = agribackup.Configuration()
configuration.api_key[ 'ApiKeyAuth' ] = 'sk_test_YOUR_API_KEY'
with agribackup.ApiClient(configuration) as api_client:
polygons_api = agribackup.EnterprisePolygonsApi(api_client)
req = agribackup.PolygonIngestionRequest(
features = [
{
"type" : "Feature" ,
"geometry" : {
"type" : "Polygon" ,
"coordinates" : [[[ 36.8 , - 1.2 ], [ 36.9 , - 1.2 ],
[ 36.9 , - 1.3 ], [ 36.8 , - 1.3 ],
[ 36.8 , - 1.2 ]]]
},
"properties" : {
"farmer_name" : "Global Coffee Farmer #1" ,
"farmer_id" : "TEST_FARMER_100" ,
"plot_name" : "Nyeri Hill Farm Block B" ,
"area" : 2.5 ,
"unit" : "HECTARES" ,
"commodity" : "Coffee"
}
}
]
)
polygon_job = polygons_api.ingest_polygons(
idempotency_key = 'tx_1001' ,
polygon_ingestion_request = req
)
print ( "Polygon job accepted:" , polygon_job.job_id)
import { AgriBackupClient } from '@agribackup/sdk' ;
const client = new AgriBackupClient ({ apiKey: 'sk_test_YOUR_API_KEY' });
const polygonJob = await client . polygons . ingestFromFile ( './farm.geojson' , {
idempotencyKey: 'tx_uuid_1002'
});
console . log ( 'Polygon job accepted:' , polygonJob . jobId );
import com.agribackup.client. * ;
import com.agribackup.client.api. * ;
import com.agribackup.client.model. * ;
import java.util.UUID;
ApiClient defaultClient = Configuration . getDefaultApiClient ();
defaultClient . setApiKey ( "sk_test_YOUR_API_KEY" );
EnterprisePolygonsApi polygonsApi = new EnterprisePolygonsApi (defaultClient);
JobAcceptedResponse polygonJob = polygonsApi . ingestPolygons (
UUID . randomUUID (), req, null
);
System . out . println ( "Polygon job accepted: " + polygonJob . getJobId ());
Expected response (202 Accepted):
{
"jobId" : "job_998877" ,
"status" : "PENDING" ,
"estimatedDurationSec" : 3 ,
"createdAt" : "2026-06-15T12:00:00Z"
}
The Location response header also points directly to GET /api/v1/enterprise/eudr/jobs/job_998877 for polling. Wait for the polygon.verified webhook or poll until phase is COMPLETED before proceeding.
Step 4 — Register the consolidated batch
Once your polygons are verified, register a commodity batch that references the polygon IDs. AgriBackup cross-checks the polygon compliance state, triggers a Copernicus risk assessment, and anchors the batch on Hedera. This step is also asynchronous and returns 202 Accepted.
import agribackup
from agribackup.rest import ApiException
configuration = agribackup.Configuration()
configuration.api_key[ 'ApiKeyAuth' ] = 'sk_test_YOUR_API_KEY'
with agribackup.ApiClient(configuration) as api_client:
batches_api = agribackup.EnterpriseBatchesApi(api_client)
batch = batches_api.register_batch(
agribackup.BatchRegistration(
batch_code = 'BR-2026-991' ,
supplier_id = 'ERP-VEND-991' ,
total_quantity = 1500.0 ,
unit = 'KGM' ,
harvest_date = '2026-07-08' ,
valid_polygon_ids = [polygon_job.polygon_id]
)
)
print ( "Batch registered:" , batch.batch_id)
const batch = await client . batches . ingest ({
batchCode: 'BR-2026-COCOA-991' ,
supplierId: 'ERP-VEND-991' ,
totalQuantity: 1500.0 ,
unit: 'KGM' ,
harvestDate: '2026-07-08' ,
validPolygonIds: [ polygonJob . polygonId ]
});
console . log ( 'Batch registered:' , batch . batchId );
import com.agribackup.client. * ;
import com.agribackup.client.api. * ;
import com.agribackup.client.model. * ;
import java.math.BigDecimal;
import java.time.LocalDate;
EnterpriseBatchesApi batchesApi = new EnterpriseBatchesApi (defaultClient);
BatchResponse batch = batchesApi . registerBatch (
new BatchRegistration ()
. batchCode ( "BR-2026-991" )
. supplierId ( "ERP-VEND-991" )
. totalQuantity ( new BigDecimal ( "1500.0" ))
. unit ( "KGM" )
. harvestDate ( LocalDate . parse ( "2026-07-08" ))
. addValidPolygonIdsItem ( polygonJob . getPolygonId ())
);
System . out . println ( "Batch registered: " + batch . getBatchId ());
Expected response (202 Accepted):
{
"jobId" : "job_112233" ,
"status" : "PENDING" ,
"estimatedDurationSec" : 5 ,
"createdAt" : "2026-06-15T12:01:00Z"
}
Step 5 — Link the shipment (Bill of Lading)
Invoke the Hedera BATCH_SHIPMENT_GATEKEEPER smart contract to cryptographically lock the compliant batch to a logistics shipment. Provide the Bill of Lading number so it is permanently recorded on-chain. This emits a shipment.linked webhook on consensus.
import agribackup
from agribackup.rest import ApiException
configuration = agribackup.Configuration()
configuration.api_key[ 'ApiKeyAuth' ] = 'sk_test_YOUR_API_KEY'
with agribackup.ApiClient(configuration) as api_client:
logistics_api = agribackup.EnterpriseLogisticsApi(api_client)
logistics_api.link_batch_to_shipment(
agribackup.ShipmentLinkRequest(
batch_id = batch.batch_id,
shipment_reference = 'BOL-99281744'
)
)
print ( "Shipment linked — awaiting consensus webhook" )
await client . logistics . linkBatchToShipment ({
batchId: batch . batchId ,
shipmentReference: 'BOL-99281744'
});
console . log ( 'Shipment linked — awaiting consensus webhook' );
import com.agribackup.client. * ;
import com.agribackup.client.api. * ;
import com.agribackup.client.model. * ;
EnterpriseLogisticsApi logisticsApi = new EnterpriseLogisticsApi (defaultClient);
logisticsApi . linkBatchToShipment (
new ShipmentLinkRequest ()
. batchId ( batch . getBatchId ())
. shipmentReference ( "BOL-99281744" )
);
System . out . println ( "Shipment linked — awaiting consensus webhook" );
Step 6 — Generate the Due Diligence Statement
Generate a TRACES NT–compliant DDS XML document. Provide SHA-256 hashes of your land tenure documents — AgriBackup anchors these hashes on Hedera as legal proof before generating the XML and filing it with TRACES NT.
import agribackup
from agribackup.rest import ApiException
configuration = agribackup.Configuration()
configuration.api_key[ 'ApiKeyAuth' ] = 'sk_test_YOUR_API_KEY'
with agribackup.ApiClient(configuration) as api_client:
try :
declarations_api = agribackup.EnterpriseDeclarationsApi(api_client)
dds = declarations_api.generate_dds(
agribackup.DdsRequest(
batch_id = batch.batch_id,
operator_details = { 'name' : 'Tier 1 Corp' , 'country' : 'KE' }
)
)
print ( "DDS Successfully Filed. Reference:" , dds.traces_nt_xml)
except ApiException as e:
print ( "Cryptographic Rejection:" , e)
import { AgriBackupClient } from '@agribackup/sdk' ;
const client = new AgriBackupClient ({ apiKey: 'sk_test_YOUR_API_KEY' });
async function executeComplianceLifecycle () {
try {
const dds = await client . declarations . generateDds ({
batchId: batch . batchId ,
operatorDetails: { name: 'Tier 1 Corp' , country: 'KE' }
});
console . log ( 'DDS Successfully Filed. Reference:' , dds . tracesNtXml );
} catch ( e ) {
console . error ( "Cryptographic Rejection:" , e . message );
}
}
import com.agribackup.client. * ;
import com.agribackup.client.api. * ;
import com.agribackup.client.model. * ;
public class ComplianceExecution {
public static void main ( String [] args ) {
ApiClient defaultClient = Configuration . getDefaultApiClient ();
defaultClient . setApiKey ( "sk_test_YOUR_API_KEY" );
try {
EnterpriseDeclarationsApi declarationsApi =
new EnterpriseDeclarationsApi (defaultClient);
DeclarationResponse dds = declarationsApi . generateDds (
new DdsRequest ()
. batchId ( batch . getBatchId ())
. operatorDetails (
new OperatorDetails (). name ( "Tier 1 Corp" ). country ( "KE" )
)
);
System . out . println ( "DDS Successfully Filed: " + dds . getTracesNtXml ());
} catch ( ApiException e ) {
System . err . println ( "Cryptographic Rejection: " + e . getResponseBody ());
}
}
}
Expected response (202 Accepted):
{
"jobId" : "job_445566" ,
"status" : "PENDING" ,
"estimatedDurationSec" : 8 ,
"createdAt" : "2026-06-15T12:03:00Z"
}
When the DDS job completes, you receive a dds.submitted webhook containing the TRACES NT reference number and the Hedera consensus transaction ID.
The complete lifecycle at a glance
Python (full lifecycle)
Node.js (full lifecycle)
Java (full lifecycle)
import agribackup
from agribackup.rest import ApiException
# 1. Initialize Cryptographic Authentication
# Base URL is automatically inferred from the API Key prefix (sk_live_ vs sk_test_)
configuration = agribackup.Configuration()
configuration.api_key[ 'ApiKeyAuth' ] = 'sk_live_YOUR_API_KEY'
with agribackup.ApiClient(configuration) as api_client:
try :
# 2. Ingest GeoJSON Polygons (Satellite Verification)
polygons_api = agribackup.EnterprisePolygonsApi(api_client)
polygon_job = polygons_api.ingest_polygons( idempotency_key = 'tx_1001' , polygon_ingestion_request = req)
# 3. Register the Consolidated Batch (Upon Oracle Compliance)
batches_api = agribackup.EnterpriseBatchesApi(api_client)
batch = batches_api.register_batch(agribackup.BatchRegistration(
batch_code = 'BR-2026-991' , supplier_id = 'ERP-VEND-991' ,
total_quantity = 1500.0 , unit = 'KGM' , harvest_date = '2026-07-08' ,
valid_polygon_ids = [polygon_job.polygon_id]
))
# 4. Link Logistics (Bill of Lading)
logistics_api = agribackup.EnterpriseLogisticsApi(api_client)
logistics_api.link_batch_to_shipment(agribackup.ShipmentLinkRequest(
batch_id = batch.batch_id, shipment_reference = 'BOL-99281744'
))
# 5. Generate Due Diligence Statement (TRACES NT)
declarations_api = agribackup.EnterpriseDeclarationsApi(api_client)
dds = declarations_api.generate_dds(agribackup.DdsRequest(
batch_id = batch.batch_id, operator_details = { 'name' : 'Tier 1 Corp' , 'country' : 'KE' }
))
print ( "DDS Successfully Filed. Reference:" , dds.traces_nt_xml)
except ApiException as e:
print ( "Cryptographic Rejection:" , e)
import { AgriBackupClient } from '@agribackup/sdk' ;
// 1. Initialize Cryptographic Authentication
// Base URL is automatically inferred from the API Key prefix
const client = new AgriBackupClient ({
apiKey: 'sk_live_YOUR_API_KEY'
});
async function executeComplianceLifecycle () {
try {
// 2. Ingest GeoJSON Polygons (Satellite Verification)
const polygonJob = await client . polygons . ingestFromFile ( './farm.geojson' , {
idempotencyKey: 'tx_uuid_1002'
});
// 3. Register the Consolidated Batch (Upon Oracle Compliance)
const batch = await client . batches . ingest ({
batchCode: 'BR-2026-COCOA-991' , supplierId: 'ERP-VEND-991' ,
totalQuantity: 1500.0 , unit: 'KGM' , harvestDate: '2026-07-08' ,
validPolygonIds: [ polygonJob . polygonId ]
});
// 4. Link Logistics (Bill of Lading)
await client . logistics . linkBatchToShipment ({
batchId: batch . batchId , shipmentReference: 'BOL-99281744'
});
// 5. Generate Due Diligence Statement (TRACES NT)
const dds = await client . declarations . generateDds ({
batchId: batch . batchId ,
operatorDetails: { name: 'Tier 1 Corp' , country: 'KE' }
});
console . log ( 'DDS Successfully Filed. Reference:' , dds . tracesNtXml );
} catch ( e ) {
console . error ( "Cryptographic Rejection:" , e . message );
}
}
import com.agribackup.client. * ;
import com.agribackup.client.api. * ;
import com.agribackup.client.model. * ;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
public class ComplianceExecution {
public static void main ( String [] args ) {
// 1. Initialize Cryptographic Authentication
// Base URL is automatically inferred from the API Key prefix
ApiClient defaultClient = Configuration . getDefaultApiClient ();
defaultClient . setApiKey ( "sk_live_YOUR_API_KEY" );
try {
// 2. Ingest GeoJSON Polygons (Satellite Verification)
EnterprisePolygonsApi polygonsApi = new EnterprisePolygonsApi (defaultClient);
JobAcceptedResponse polygonJob = polygonsApi . ingestPolygons ( UUID . randomUUID (), req, null );
// 3. Register the Consolidated Batch (Upon Oracle Compliance)
EnterpriseBatchesApi batchesApi = new EnterpriseBatchesApi (defaultClient);
BatchResponse batch = batchesApi . registerBatch (
new BatchRegistration (). batchCode ( "BR-2026-991" ). supplierId ( "ERP-VEND-991" )
. totalQuantity ( new BigDecimal ( "1500.0" )). unit ( "KGM" ). harvestDate ( LocalDate . parse ( "2026-07-08" ))
. addValidPolygonIdsItem ( polygonJob . getPolygonId ())
);
// 4. Link Logistics (Bill of Lading)
EnterpriseLogisticsApi logisticsApi = new EnterpriseLogisticsApi (defaultClient);
logisticsApi . linkBatchToShipment (
new ShipmentLinkRequest (). batchId ( batch . getBatchId ()). shipmentReference ( "BOL-99281744" )
);
// 5. Generate Due Diligence Statement (TRACES NT)
EnterpriseDeclarationsApi declarationsApi = new EnterpriseDeclarationsApi (defaultClient);
DeclarationResponse dds = declarationsApi . generateDds (
new DdsRequest (). batchId ( batch . getBatchId ())
. operatorDetails ( new OperatorDetails (). name ( "Tier 1 Corp" ). country ( "KE" ))
);
System . out . println ( "DDS Successfully Filed: " + dds . getTracesNtXml ());
} catch ( ApiException e ) {
System . err . println ( "Cryptographic Rejection: " + e . getResponseBody ());
}
}
}
Next steps
Authentication Learn about live vs sandbox keys, base URL resolution, and securing your credentials.
Async Architecture Understand the job and webhook model behind every 202 response.
Cryptographic Evidence Retrieve and verify Hedera state proofs for auditors and customs authorities.
EUDR Compliance Understand the regulation your integration must satisfy.