Create Logistics Request
Calculate CO2 equivalent (CO2e) emissions for a freight shipment with flexible parameters for origin/destination or direct distance input.
New API : This endpoint is part of the new API architecture with improved design and maintainability.
Request
Your API key for authentication Example: sk_live_1234567890abcdef
Your organization UUID Example: a8315ef3-dd50-43f8-b7ce-d839e68d51fa
Body Parameters
Origin location of the shipment (city and country). Required if distance_km is not provided. Example: "Madrid, Spain"
Destination location of the shipment. Required if distance_km is not provided. Example: "Barcelona, Spain"
Distance in kilometers between origin and destination. If provided, takes priority over origin and destination. Example: 500
Load of the shipment. If not provided, it will be calculated from the TOC or category. Example: 1000
Unit of the load. Available values: kg, ton, pallets, teu, feuDefault: kg
Factor to divide the load by (between 0 and 1). Example: 100 pallets with 0.5 factor is 50 pallets. Default: 1.0
Type of vehicle to use for calculation. If omitted, it will be estimated from the category provided. Example: "van_diesel"
Category of the vehicle. Required if toc is not provided. Available values: road, rail, maritime, airExample: "road"
Country ISO 3166-2 code of the origin to use the regionβs emission factors. If not provided, global factors will be used. Default: GLOExample: ES
Year of the emission factor to use. If not provided, current year will be used. Example: 2024
If cleaning has been done, it will increase the emissions. Default: false
Traceability Parameters
These optional parameters allow you to track shipments with full traceability for logistics providers.
Unique identifier for the shipment movement, used for tracking and deduplication. Example: "MOV-2024-001234"
Client or customer identifier for the shipment. Example: "CORREOS-EXPRESS"
Date of the shipment (ISO 8601 format). If not provided, current date will be used. Example: "2024-11-25"
Stretch or segment of the movement (e.g., first mile, last mile). Example: "last_mile"
Stage of the movement in the logistics chain. Example: "delivery"
License plate of the vehicle used for the shipment. Example: "1234-ABC"
License plate of the trailer, if applicable. Example: "5678-XYZ"
Whether the shipment is handled by a subcontractor. Default: false
Tonne-kilometers (load in tonnes multiplied by distance in km). If provided, used directly for calculations instead of computing from load and distance. Example: 500.0
Identifier of the logistics hub associated with this shipment. Example: "HUB-MAD-001"
Package Parameters (Multi-leg Shipments)
These parameters enable tracking of packages that travel through multiple legs.
Unique identifier for the package. When provided, this leg will be associated with the package. Requires movement_id to group packages into shipments. Example: "26830007899150601093463"
Package Tracking : When you provide package_key, the system automatically creates or finds the package and associates this leg with it. Multiple legs with the same package_key will be grouped together, and the packageβs total distance and emissions will be aggregated.
Response
Unique identifier for the logistics request
Total CO2 equivalent emissions in kilograms
Timestamp when the request was created
Timestamp when the request was last updated
Distance in kilometers (if provided or calculated)
Type of vehicle (if provided)
Year used for emission factors
Unique identifier for the shipment movement
Client or customer identifier
Stretch or segment of the movement
Stage of the movement in the logistics chain
License plate of the vehicle
License plate of the trailer
Whether handled by a subcontractor
Tonne-kilometers calculated or provided
UUID of the associated package (if package_key was provided)
Package identifier (if provided in request)
Example
curl -X POST "https://api.dcycle.io/v1/logistics/requests" \
-H "x-api-key: ${ DCYCLE_API_KEY }" \
-H "x-organization-id: ${ DCYCLE_ORG_ID }" \
-H "Content-Type: application/json" \
-d '{
"origin": "Madrid, Spain",
"destination": "Barcelona, Spain",
"load": 1000,
"load_unit": "kg",
"category": "road"
}'
Successful Response
{
"id" : "0b08bffd-a1e3-48cb-a477-aefa59131934" ,
"origin" : "CALLE MAESTRO FALLA 11, SALAMANCA, 37003" ,
"destination" : "C/ MONTERA, 25-27 - P.I. LOS VILLARES, VILLARES DE LA REINA, 37184" ,
"origin_country" : "ES" ,
"distance_km" : 3.4447 ,
"load" : 1.0 ,
"load_unit" : "kg" ,
"load_factor" : 1.0 ,
"toc" : "generic_average_road" ,
"category" : "road" ,
"year" : 2025 ,
"cleaning" : false ,
"co2e" : 0.000528 ,
"tkm" : 0.00344 ,
"movement_id" : "9883000788469370" ,
"client" : "AMAZON" ,
"shipment_date" : "2025-05-19" ,
"movement_stretch" : "RECOGIDA" ,
"movement_stage" : "FIRST_MILE" ,
"package_key" : "98830007884693701030068" ,
"package_id" : "128be1de-9aa0-4c7d-891f-5daf5467107e" ,
"subcontractor" : false ,
"vehicle_license_plate" : null ,
"trailer_license_plate" : null ,
"hub_id" : null ,
"created_at" : "2025-12-04T06:42:35.973485" ,
"updated_at" : "2025-12-04T06:42:35.973489"
}
Common Errors
401 Unauthorized
Cause: Missing or invalid API key
{
"detail" : "Invalid API key" ,
"code" : "INVALID_API_KEY"
}
Solution: Verify your API key is valid and active. Get a new one from Settings β API .
404 Not Found
Cause: Organization not found
{
"code" : "ORGANIZATION_NOT_FOUND" ,
"detail" : "Organization with id=UUID('...') not found"
}
Solution: Verify that the x-organization-id header contains a valid organization UUID.
422 Validation Error
Cause: Missing required parameters or invalid values
{
"detail" : [
{
"loc" : [ "body" , "category" ],
"msg" : "Please provide a category if toc is not present" ,
"type" : "value_error"
}
]
}
Solution: Ensure that either toc or category is provided. If using addresses, provide both origin and destination, or use distance_km instead.
Use Cases
Calculate Emissions with Addresses
Most common use case - let the API calculate distance from addresses:
def calculate_shipment_emissions (origin, destination, load_kg):
"""Calculate emissions for a shipment between two locations"""
response = requests.post(
"https://api.dcycle.io/v1/logistics/requests" ,
headers = headers,
json = {
"origin" : origin,
"destination" : destination,
"load" : load_kg,
"load_unit" : "kg" ,
"category" : "road"
}
)
result = response.json()
return {
"co2e" : result[ "co2e" ],
"distance" : result.get( "distance_km" )
}
# Example usage
emissions = calculate_shipment_emissions(
origin = "Madrid, Spain" ,
destination = "Barcelona, Spain" ,
load_kg = 1500
)
print ( f "Emissions: { emissions[ 'co2e' ] } kg CO2e" )
Using Known Distance
If you already know the distance, skip geocoding:
def calculate_with_distance (distance_km, load_kg, country = "GLO" ):
"""Calculate emissions when distance is already known"""
response = requests.post(
"https://api.dcycle.io/v1/logistics/requests" ,
headers = headers,
json = {
"distance_km" : distance_km,
"load" : load_kg,
"load_unit" : "kg" ,
"category" : "road" ,
"origin_country" : country # For regional emission factors
}
)
return response.json()
# Example: 500km shipment in Spain
result = calculate_with_distance( 500 , 1000 , "ES" )
print ( f "CO2e: { result[ 'co2e' ] } kg" )
Comparing Transport Modes
Compare emissions across different transport categories:
def compare_transport_modes (origin, destination, load):
"""Compare emissions for different transport modes"""
modes = [ "road" , "rail" , "air" , "maritime" ]
results = {}
for mode in modes:
response = requests.post(
"https://api.dcycle.io/v1/logistics/requests" ,
headers = headers,
json = {
"origin" : origin,
"destination" : destination,
"load" : load,
"load_unit" : "kg" ,
"category" : mode
}
)
if response.status_code == 200 :
data = response.json()
results[mode] = data[ "co2e" ]
return results
# Compare modes for Barcelona to Madrid shipment
comparison = compare_transport_modes(
"Barcelona, Spain" ,
"Madrid, Spain" ,
1000
)
for mode, emissions in comparison.items():
print ( f " { mode } : { emissions :.2f } kg CO2e" )
Logistics Provider Integration (Full Traceability)
For logistics providers like courier companies that need complete shipment tracking:
def create_tracked_shipment (shipment_data):
"""Create a shipment with full traceability for logistics providers"""
response = requests.post(
"https://api.dcycle.io/v1/logistics/requests" ,
headers = headers,
json = {
# Core emission parameters
"origin" : shipment_data[ "origin" ],
"destination" : shipment_data[ "destination" ],
"load" : shipment_data[ "weight_kg" ],
"load_unit" : "kg" ,
"category" : "road" ,
"origin_country" : "ES" ,
# Traceability parameters
"movement_id" : shipment_data[ "tracking_number" ],
"client" : shipment_data[ "client_code" ],
"shipment_date" : shipment_data[ "date" ],
"movement_stretch" : shipment_data.get( "stretch" , "delivery" ),
"movement_stage" : shipment_data.get( "stage" , "last_mile" ),
"vehicle_license_plate" : shipment_data.get( "vehicle_plate" ),
"trailer_license_plate" : shipment_data.get( "trailer_plate" ),
"subcontractor" : shipment_data.get( "is_subcontracted" , False ),
}
)
return response.json()
# Example: Correos Express shipment
shipment = create_tracked_shipment({
"tracking_number" : "CE-2024-987654" ,
"client_code" : "CORREOS-EXPRESS" ,
"origin" : "Centro Logistico Madrid, Spain" ,
"destination" : "Barcelona, Spain" ,
"weight_kg" : 25 ,
"date" : "2024-11-25" ,
"stretch" : "hub_to_destination" ,
"stage" : "delivery" ,
"vehicle_plate" : "1234-ABC" ,
"is_subcontracted" : False
})
print ( f "Tracking: { shipment[ 'movement_id' ] } " )
print ( f "CO2e: { shipment[ 'co2e' ] } kg" )
Bulk Shipment Processing
Process multiple shipments efficiently:
def process_daily_shipments (shipments: list ):
"""Process a batch of shipments and return total emissions"""
results = []
total_co2e = 0
for shipment in shipments:
result = create_tracked_shipment(shipment)
results.append(result)
total_co2e += result[ "co2e" ]
return {
"shipments_processed" : len (results),
"total_co2e_kg" : total_co2e,
"results" : results
}
Single Package Tracking
Track a single package with one leg (simplest case):
# Create a leg associated with a package
response = requests.post(
"https://api.dcycle.io/v1/logistics/requests" ,
headers = headers,
json = {
"origin" : "Madrid Hub" ,
"destination" : "Customer Address, Madrid" ,
"load" : 5 ,
"load_unit" : "kg" ,
"category" : "road" ,
# Package tracking
"movement_id" : "SHIP-2024-001" , # Shipment identifier
"package_key" : "PKG-2024-000001" , # Unique package identifier
# Optional
"client" : "AMAZON" ,
"shipment_date" : "2024-11-25" ,
}
)
result = response.json()
print ( f "Package { result[ 'package_key' ] } - CO2e: { result[ 'co2e' ] } kg" )
Multiple Packages in One Shipment
Track multiple packages belonging to the same shipment. Each package has its own package_key but shares the same movement_id:
def create_shipment_with_packages (movement_id: str , packages: list ):
"""
Create a shipment with multiple packages.
Each package is a single-leg delivery in this example.
"""
results = []
for pkg in packages:
response = requests.post(
"https://api.dcycle.io/v1/logistics/requests" ,
headers = headers,
json = {
"origin" : pkg[ "origin" ],
"destination" : pkg[ "destination" ],
"load" : pkg[ "weight_kg" ],
"load_unit" : "kg" ,
"category" : "road" ,
# Same movement_id groups all packages into one shipment
"movement_id" : movement_id,
# Each package has unique key
"package_key" : pkg[ "package_key" ],
"client" : pkg.get( "client" , "DEFAULT" ),
}
)
results.append(response.json())
return results
# Example: Shipment with 3 packages going to different destinations
packages = [
{ "package_key" : "PKG-001" , "origin" : "Madrid Hub" , "destination" : "Address A, Madrid" , "weight_kg" : 2 , "client" : "AMAZON" },
{ "package_key" : "PKG-002" , "origin" : "Madrid Hub" , "destination" : "Address B, Madrid" , "weight_kg" : 5 , "client" : "AMAZON" },
{ "package_key" : "PKG-003" , "origin" : "Madrid Hub" , "destination" : "Address C, Madrid" , "weight_kg" : 1 , "client" : "AMAZON" },
]
results = create_shipment_with_packages( "SHIP-2024-100" , packages)
total_co2e = sum (r[ 'co2e' ] for r in results)
print ( f "Shipment SHIP-2024-100: {len (results) } packages, { total_co2e :.2f } kg CO2e total" )
# Later, retrieve all packages for this shipment:
# GET /v1/logistics/packages?movement_id=SHIP-2024-100
Multi-leg Package Tracking
Track a package through multiple legs of its journey. Each leg is created separately, and the system automatically aggregates emissions per package:
def track_package_journey (package_key: str , movement_id: str , legs: list ):
"""
Track a package through multiple legs.
Args:
package_key: Unique package identifier
movement_id: Shipment identifier (groups packages)
legs: List of leg data (origin, destination, load, etc.)
"""
results = []
for leg in legs:
response = requests.post(
"https://api.dcycle.io/v1/logistics/requests" ,
headers = headers,
json = {
# Core parameters
"origin" : leg[ "origin" ],
"destination" : leg[ "destination" ],
"load" : leg[ "load_kg" ],
"load_unit" : "kg" ,
"category" : leg.get( "category" , "road" ),
# Package tracking
"package_key" : package_key,
"movement_id" : movement_id,
# Optional traceability
"client" : leg.get( "client" ),
"shipment_date" : leg.get( "date" ),
}
)
results.append(response.json())
return results
# Example: Package traveling through 3 legs
legs = [
{ "origin" : "Madrid Hub" , "destination" : "Valencia Hub" , "load_kg" : 15 },
{ "origin" : "Valencia Hub" , "destination" : "Barcelona Hub" , "load_kg" : 15 },
{ "origin" : "Barcelona Hub" , "destination" : "Final Address, Barcelona" , "load_kg" : 15 },
]
results = track_package_journey(
package_key = "PKG-2024-123456" ,
movement_id = "SHIP-2024-001" ,
legs = legs
)
# Get aggregated package data
package_response = requests.get(
f "https://api.dcycle.io/v1/logistics/packages?movement_id=SHIP-2024-001" ,
headers = headers
)
print ( f "Total package emissions: { package_response.json()[ 'items' ][ 0 ][ 'total_co2e' ] } kg CO2e" )
Migration from Legacy API
If youβre migrating from the legacy /api/v1/logistics/shipment endpoint:
URL Change Old: /api/v1/logistics/shipmentNew: /v1/logistics/requests
Headers Update Removed: x-user-id (not needed)Keep: Authorization, x-organization-id
Response Changes Added: id field for trackingAdded: created_at, updated_at timestamps
Field Names Most field names remain the same Check validation rules for category vs toc
Both APIs will be supported during the migration period. New integrations should use /v1/logistics/requests.