Skip to main content

Understanding Transport Operations in GLEC

In the GLEC Framework, every shipment is broken down into transport legs — individual segments where goods move from one point to another using a specific vehicle type. Each leg is classified by its TOC (Transport Operation Category), which determines the emission factor applied.
┌──────────────────────────────────────────────────────────────────────────────┐
│                        TRANSPORT OPERATION FLOW                              │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  Shipment: Madrid → Hamburg (multi-modal)                                   │
│                                                                              │
│  Leg 1 (Road)           Leg 2 (Maritime)         Leg 3 (Road)              │
│  ─────────────          ────────────────         ─────────────              │
│  Madrid → Barcelona     Barcelona → Hamburg      Hamburg → Warehouse        │
│  TOC: artic_truck       TOC: general_cargo       TOC: van_diesel           │
│  500 km · 12t           1800 km · 12t            30 km · 12t               │
│                                                                              │
│  CO2e = tkm × WTW      CO2e = tkm × WTW         CO2e = tkm × WTW         │
│                                                                              │
│  Total shipment CO2e = Leg 1 + Leg 2 + Leg 3                               │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘
GLEC Calculation FormulaFor each transport leg:CO2e (kg) = tkm x WTW emission factor (kgCO2e/tkm)Where:
  • tkm = load (tonnes) x distance (km)
  • WTW = Well-to-Wheel factor from the TOC (includes fuel production + combustion)
  • For electric vehicles: CO2e = tkm x grid emission factor x energy efficiency factor

Prerequisites

Before starting, ensure you have:
  • Dcycle API credentials (get them here)
  • An organization configured in Dcycle
  • Shipment data: origins, destinations, weights, and vehicle types (or at least transport category)

Step 1.1: Explore Available TOCs

Before creating transport legs, list the available vehicle types and their emission factors:
No input data required. Returns all available TOCs with their WTW factors and default loads.Response fields:
FieldDescriptionExample
tocVehicle identifier (vehicle_type)"van_diesel"
categoryTransport mode"road"
vehicleVehicle type"van"
typeFuel/specification"diesel"
wtwWTW factor (kgCO2e/tkm)0.196
default_loadDefault capacity (kg)3500.0
locationRegion"EU"
import requests
import os

headers = {
    "Authorization": f"Bearer {os.getenv('DCYCLE_API_KEY')}",
    "Content-Type": "application/json",
    "x-organization-id": os.getenv("DCYCLE_ORG_ID"),
    "x-user-id": os.getenv("DCYCLE_USER_ID"),
}

# List all available TOCs
tocs = requests.get(
    "https://api.dcycle.io/v1/logistics/tocs",
    headers=headers,
).json()

print("Available Transport Operation Categories:")
for toc in tocs:
    print(f"  {toc['toc']:30s} | {toc['category']:10s} | WTW: {toc['wtw']:.4f} kgCO2e/tkm | Region: {toc['location']}")

Common TOCs by Transport Mode

TOCVehicleFuelTypical Use
van_dieselVanDieselUrban/last-mile deliveries
van_electricVanElectricZero-emission urban deliveries
rigid_truck_dieselRigid truckDieselRegional freight
artic_truck_dieselArticulated truckDieselLong-haul freight
artic_truck_electricArticulated truckElectricElectric long-haul
tank_truck_dieselTank truckDieselLiquid bulk transport
generic_average_roadGenericAverageWhen vehicle type is unknown
Road fuels supported: diesel, electric, CNG, LNG, HVO, biodiesel blends, LPG.
Don’t know the exact vehicle type? Provide only category (e.g., "road", "maritime") instead of toc, and Dcycle will use the generic average TOC for that mode and region.

Step 1.2: Create a Single Transport Leg

FieldTypeRequiredDescriptionExample
originstringConditionalOrigin address (required if no distance_km)"Madrid, Spain"
destinationstringConditionalDestination address (required if no distance_km)"Barcelona, Spain"
distance_kmnumberConditionalDistance in km (used as-is, overrides geocoding)620
tocstringConditionalTOC identifier (if omitted, provide category)"rigid_truck_diesel"
categorystringConditionalTransport mode (if toc is omitted)"road"
origin_countrystringNoISO 3166-2 code for region-specific factors"ES"
loadnumberNoCargo weight (defaults to TOC’s default_load)5000
load_unitstringNoUnit: kg, ton, pallets, teu, feu"kg"
load_factornumberNoAllocation factor 0-1 (e.g., 0.5 = half truck)0.5
yearnumberNoEmission factor year (defaults to current)2025
cleaningbooleanNoVehicle cleaning done (adds cleaning emissions)false
subcontractorbooleanNoHandled by a subcontractor (affects scope allocation)false
clientstringNoClient identifier for reporting"AMAZON"
movement_idstringNoGroups multiple legs of the same shipment"MOV-2025-001"
shipment_datedateNoDate of shipment (YYYY-MM-DD)"2025-03-15"
hub_idstringNoHub associated with this leg"HUB-MAD-001"
package_keystringNoPackage identifier (requires movement_id)"PKG-12345"
Where to get this data:
  • toc: Use GET /v1/logistics/tocs to list available vehicle types
  • origin/destination: Physical addresses or city names (geocoded automatically)
  • load: From your shipping manifest, bill of lading, or TMS
  • client: From your customer management system

Basic shipment (addresses + TOC)

# Calculate a single road shipment
shipment = {
    "origin": "Calle Gran Via 1, Madrid, Spain",
    "destination": "Passeig de Gracia 50, Barcelona, Spain",
    "origin_country": "ES",
    "toc": "rigid_truck_diesel",
    "load": 8000,
    "load_unit": "kg",
    "year": 2025,
    "client": "RETAILCO",
    "shipment_date": "2025-03-15",
}

response = requests.post(
    "https://api.dcycle.io/v1/logistics/requests",
    headers=headers,
    json=shipment,
).json()

print(f"Request ID: {response['id']}")
print(f"Distance: {response['distance_km']:.1f} km (with DAF applied)")
print(f"CO2e: {response['co2e']:.2f} kg")
print(f"TOC: {response['toc']}")

Using known distance (skip geocoding)

When you already have the distance, provide distance_km directly:
# Known distance — geocoding is skipped
shipment = {
    "distance_km": 620,
    "origin_country": "ES",
    "toc": "artic_truck_diesel",
    "load": 15,
    "load_unit": "ton",
    "year": 2025,
}

response = requests.post(
    "https://api.dcycle.io/v1/logistics/requests",
    headers=headers,
    json=shipment,
).json()

print(f"CO2e: {response['co2e']:.2f} kg")

Using tkm directly

If you already know the tonne-kilometers, provide tkm:
# Known tkm — skip distance and load calculation
shipment = {
    "tkm": 9300,  # 15 tonnes x 620 km
    "origin_country": "ES",
    "toc": "artic_truck_diesel",
    "year": 2025,
}

response = requests.post(
    "https://api.dcycle.io/v1/logistics/requests",
    headers=headers,
    json=shipment,
).json()

Using category instead of TOC

When you don’t know the exact vehicle type:
# Only know it's road transport
shipment = {
    "origin": "Madrid, Spain",
    "destination": "Sevilla, Spain",
    "category": "road",  # Will use generic_average_road for the region
    "load": 3000,
    "load_unit": "kg",
}

response = requests.post(
    "https://api.dcycle.io/v1/logistics/requests",
    headers=headers,
    json=shipment,
).json()

print(f"TOC selected: {response['toc']}")
print(f"CO2e: {response['co2e']:.2f} kg")

Step 1.3: Multi-Modal and Multi-Leg Shipments

Use movement_id to group multiple legs into a single shipment, and package_key to track individual packages across legs:
movement_id = "MOV-2025-MAD-HAM-001"

# Leg 1: Road (first mile)
leg1 = {
    "origin": "Madrid, Spain",
    "destination": "Barcelona Port, Spain",
    "origin_country": "ES",
    "toc": "artic_truck_diesel",
    "load": 12000,
    "load_unit": "kg",
    "movement_id": movement_id,
    "movement_stretch": "first_mile",
    "package_key": "PKG-26830007899",
    "client": "IMPORT-CO",
}

# Leg 2: Maritime (main haul)
leg2 = {
    "origin": "Barcelona, Spain",
    "destination": "Hamburg, Germany",
    "origin_country": "ES",
    "toc": "general_cargo_average_maritime",
    "load": 12000,
    "load_unit": "kg",
    "movement_id": movement_id,
    "movement_stretch": "main_haul",
    "package_key": "PKG-26830007899",
    "client": "IMPORT-CO",
}

# Leg 3: Road (last mile)
leg3 = {
    "origin": "Hamburg Port, Germany",
    "destination": "Hamburg Warehouse, Germany",
    "origin_country": "DE",
    "toc": "van_diesel",
    "load": 12000,
    "load_unit": "kg",
    "movement_id": movement_id,
    "movement_stretch": "last_mile",
    "package_key": "PKG-26830007899",
    "client": "IMPORT-CO",
}

total_co2e = 0
for i, leg in enumerate([leg1, leg2, leg3], 1):
    result = requests.post(
        "https://api.dcycle.io/v1/logistics/requests",
        headers=headers,
        json=leg,
    ).json()
    total_co2e += result["co2e"]
    print(f"Leg {i} ({leg['movement_stretch']}): {result['co2e']:.2f} kg CO2e | {result['distance_km']:.0f} km")

print(f"\nTotal shipment CO2e: {total_co2e:.2f} kg")
Package tracking: When package_key is provided, Dcycle creates or updates a LogisticPackage that aggregates total_distance_km and total_co2e across all legs. Retrieve package details with GET /v1/logistics/packages/{package_id}.

Subcontracted Transport

Mark legs handled by a subcontractor. These emissions are classified as Scope 3 in the GLEC company report (instead of Scope 1 for own fleet):
subcontracted_leg = {
    "origin": "Madrid, Spain",
    "destination": "Lisbon, Portugal",
    "origin_country": "ES",
    "toc": "artic_truck_diesel",
    "load": 20,
    "load_unit": "ton",
    "subcontractor": True,  # Scope 3 in GLEC report
    "client": "CLIENTCO",
}

response = requests.post(
    "https://api.dcycle.io/v1/logistics/requests",
    headers=headers,
    json=subcontracted_leg,
).json()
Own fleet vs Subcontracted — Scope allocation
Own FleetSubcontracted
TTW (direct)Scope 1Scope 3
WTT (upstream)Scope 3Scope 3
Total WTWSplit Scope 1 + 3Scope 3
Set subcontractor: true for any leg not operated by your own fleet.

Step 1.4: Load Units and Allocation

GLEC supports multiple load units. Dcycle converts them to kg internally:
UnitConversionTypical Use
kg1 kgDefault, most common
ton1 ton = 1,000 kgHeavy freight
pallets1 pallet = 1,500 kgPalletized goods
teu1 TEU = 20,000 kgContainer shipping (20ft)
feu1 FEU = 40,000 kgContainer shipping (40ft)

Load Factor (Allocation)

Use load_factor to account for partial loads or shared capacity:
# Half-truck load
shipment = {
    "origin": "Madrid, Spain",
    "destination": "Valencia, Spain",
    "toc": "artic_truck_diesel",
    "load": 100,
    "load_unit": "pallets",
    "load_factor": 0.5,  # Only using 50% of capacity → 50 pallets
}

Step 1.5: Bulk Upload (Up to 5,000 Records)

For high-volume operations, use the bulk endpoint to process up to 5,000 transport legs in a single request:
FieldTypeRequiredDescription
recordsarrayYesArray of transport leg objects (same schema as single request)
options.continue_on_errorbooleanNoContinue processing if some records fail (default: true)
Each record in the records array uses the same fields as the single request in Step 1.2.
# Bulk upload multiple transport legs
bulk_payload = {
    "records": [
        {
            "origin": "Madrid, Spain",
            "destination": "Barcelona, Spain",
            "toc": "rigid_truck_diesel",
            "load": 5000,
            "load_unit": "kg",
            "client": "CLIENT-A",
            "shipment_date": "2025-03-01",
        },
        {
            "origin": "Barcelona, Spain",
            "destination": "Valencia, Spain",
            "toc": "van_diesel",
            "load": 1200,
            "load_unit": "kg",
            "client": "CLIENT-A",
            "shipment_date": "2025-03-01",
        },
        {
            "origin": "Madrid, Spain",
            "destination": "Sevilla, Spain",
            "toc": "artic_truck_diesel",
            "load": 18,
            "load_unit": "ton",
            "client": "CLIENT-B",
            "shipment_date": "2025-03-02",
        },
    ],
    "options": {
        "continue_on_error": True,
    },
}

response = requests.post(
    "https://api.dcycle.io/v1/logistics/requests/bulk",
    headers=headers,
    json=bulk_payload,
).json()

print(f"Total received: {response['total_received']}")
print(f"Success: {response['success']}")
print(f"Failed: {response['failed']}")

# Check individual results
for result in response["results"]:
    print(f"  Record {result['index']}: {result['co2e']:.2f} kg CO2e (ID: {result['id']})")

# Check errors
for error in response["errors"]:
    print(f"  Error at record {error['index']}: {error['error']}")
Bulk limits: Maximum 5,000 records per request. For larger datasets, split into multiple bulk requests or use the legacy CSV upload endpoint (POST /logistics/upload).

Step 1.6: Query Transport Requests

Retrieve your transport legs with pagination and filtering:
# Get paginated transport requests
response = requests.get(
    "https://api.dcycle.io/v1/logistics/requests",
    headers=headers,
    params={
        "page": 1,
        "size": 50,
    },
).json()

print(f"Total requests: {response['total']}")
for req in response["items"]:
    print(f"  {req['id'][:8]}... | {req['origin']}{req['destination']} | {req['co2e']:.2f} kg CO2e")

Query Packages

Track multi-leg shipments via packages:
# Get all packages for a specific client
packages = requests.get(
    "https://api.dcycle.io/v1/logistics/packages",
    headers=headers,
    params={
        "client": "IMPORT-CO",
        "page": 1,
        "size": 50,
    },
).json()

for pkg in packages["items"]:
    print(f"Package {pkg['package_key']}: {pkg['total_co2e']:.2f} kg CO2e | {pkg['total_distance_km']:.0f} km")

# Get a single package with all its legs
package_detail = requests.get(
    f"https://api.dcycle.io/v1/logistics/packages/{packages['items'][0]['id']}",
    headers=headers,
).json()

print(f"\nPackage legs:")
for leg in package_detail["legs"]:
    print(f"  {leg['origin']}{leg['destination']}: {leg['co2e']:.2f} kg CO2e")

List Clients

# Get all client names for the organization
clients = requests.get(
    "https://api.dcycle.io/v1/logistics/clients",
    headers=headers,
).json()

print("Clients with logistics data:")
for client_name in clients:
    print(f"  - {client_name}")

Best Practices

1. Choose the Most Specific TOC

More specific TOCs yield more accurate results:
Least accurate                                        Most accurate
generic_average_road  →  rigid_truck_diesel  →  rigid_truck_hvo_blend
When you know the fuel type and vehicle class, always use the specific TOC.

2. Provide Origin Country

Always include origin_country for region-specific emission factors:
# Without origin_country → uses GLO (global) factors
shipment = {"category": "road", "distance_km": 500, "load": 5000, "load_unit": "kg"}

# With origin_country → uses EU-specific factors (more accurate)
shipment = {"category": "road", "distance_km": 500, "load": 5000, "load_unit": "kg", "origin_country": "ES"}

3. Use Movement IDs Consistently

Group related legs with movement_id for accurate shipment-level reporting and deduplication.

4. Mark Subcontracted Legs

Always set subcontractor: true for legs not operated by your own fleet — this ensures correct scope allocation in the GLEC report.

Next Steps