> ## Documentation Index
> Fetch the complete documentation index at: https://code.dcycle.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Logistics & Transport Emissions Tutorial

> Calculate transport emissions following ISO 14083 standard for accurate Scope 3 tracking

# Logistics & Transport Emissions Tutorial

Learn how to calculate emissions from transport operations using Dcycle's ISO 14083-compliant Logistics API.

<Info>
  **Estimated time**: 25 minutes

  **What you'll learn**:

  * Calculate single shipment emissions
  * Understand ISO 14083 methodology
  * Upload bulk shipment data
  * Track emissions by client and route
  * Generate logistics reports
</Info>

## Prerequisites

Before starting, ensure you have:

* Dcycle API credentials ([get them here](/docs/quickstart#step-1-get-your-api-key))
* Basic knowledge of Python or JavaScript
* Shipment data: origin, destination, weight, transport mode

<Note>
  **New API Available!** We recommend using the new `/v1/logistics/requests` endpoints for new integrations:

  * [Create Single Request](/api-reference/logistics/create-request) - Calculate emissions for one shipment
  * [Create Bulk Requests](/api-reference/logistics/create-requests-bulk) - Process up to 5,000 shipments per request

  The new API offers simplified authentication, better error handling, and optimized bulk processing.
</Note>

<Tip>
  **Using the Dcycle App?**

  You can also track logistics emissions through our web interface:

  * [Upload sent goods](https://scribehow.com/shared/How_to_upload_SHIPPED_goods_manually_or_automatically__XEzujPvPTjiM16-z5K9Pvw) ([ES](https://scribehow.com/shared/Como_subir_mercancia_enviada_de_forma_manual_o_automatica__H5mmUALDSzK7Fp00ywuJ1g)) - For outbound shipments
  * [Upload received goods](https://scribehow.com/shared/How_to_upload_received_merchandise_manually_or_automatically__WpkPx0TCT5-9KQEMmVVm5g) ([ES](https://scribehow.com/shared/Como_subir_mercancia_recibida_de_forma_manual_o_automatica__U5tGaWbnRA2rsxpve8maiQ)) - For inbound shipments
  * [Measure in logistics companies (GLEC v3.0)](https://scribehow.com/shared/Measure_Your_Footprint_in_Logistics_Companies_Under_the_GLEC_v30_Framework__pu1o2COeQ1WWNczGHMgqjw) ([ES](https://scribehow.com/shared/Mide_tu_huella_en_empresas_de_logistica_bajo_el_marco_GLEC_v30__ZzztbxeHQUmU4-PYIzgB9Q)) - For logistics service providers
</Tip>

## Understanding Transport Emissions

Logistics emissions are typically **Scope 3** (upstream or downstream transportation in your value chain).

### ISO 14083 Standard

Dcycle follows [ISO 14083](https://www.iso.org/standard/78864.html), the international standard for quantifying and reporting GHG emissions from transport operations.

**Key features**:

* **Well-to-Wheel (WTW)** methodology: Includes fuel production + vehicle operation
* **Distance-based** calculation: Accurate route distance with geocoding
* **Load factor** consideration: Accounts for vehicle capacity utilization
* **Multi-modal** support: Road, rail, sea, air, and intermodal transport

### Transport Calculation Flow

```
Shipment Data → Geocoding → Distance Calculation → TOC Selection → Emission Factors → CO2e
     ↓              ↓              ↓                    ↓                ↓            ↓
  Origin/         Lat/Lng       Route km         Vehicle type      WTW factors    Result
  Destination                                     & fuel
```

### WTW (Well-to-Wheel) Breakdown

<Steps>
  <Step title="WTT (Well-to-Tank)">
    **Upstream emissions**: Fuel extraction, refining, and distribution

    Example: 0.5 kg CO2e per liter of diesel
  </Step>

  <Step title="TTW (Tank-to-Wheel)">
    **Direct emissions**: Fuel combustion in vehicle

    Example: 2.7 kg CO2e per liter of diesel
  </Step>

  <Step title="Total WTW">
    **Complete footprint**: WTT + TTW

    Example: 3.2 kg CO2e per liter of diesel
  </Step>
</Steps>

## Step 1: Calculate a Single Shipment

Start with a simple shipment calculation:

<CodeGroup>
  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import requests
  import os

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

  # Calculate shipment emissions
  shipment = {
      "origin": "Madrid, Spain",
      "destination": "Barcelona, Spain",
      "toc": "rigid_diesel",  # Transport Operation Category
      "load": 5000,           # Weight in kg
      "load_unit": "kg",
      "year": 2024
  }

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

  print(f"✅ Shipment calculated")
  print(f"   Distance: {response['distance_km']:.1f} km")
  print(f"   CO2e (WTW): {response['co2e']:.2f} kg")
  print(f"   - TTW (direct): {response['co2e_ttw']:.2f} kg")
  print(f"   - WTT (upstream): {response['co2e_wtw']:.2f} kg")
  print(f"   Intensity: {response['co2e']/response['distance_km']:.3f} kg CO2e/km")
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const axios = require('axios');

  const headers = {
    'Authorization': `Bearer ${process.env.DCYCLE_API_KEY}`,
    'x-organization-id': process.env.DCYCLE_ORG_ID,
    'x-user-id': process.env.DCYCLE_USER_ID
  };

  // Calculate shipment emissions
  const shipment = {
    origin: 'Madrid, Spain',
    destination: 'Barcelona, Spain',
    toc: 'rigid_diesel',
    load: 5000,
    load_unit: 'kg',
    year: 2024
  };

  const response = await axios.post(
    'https://api.dcycle.io/v1/logistics/calculate',
    shipment,
    { headers }
  ).then(res => res.data);

  console.log('✅ Shipment calculated');
  console.log(`   Distance: ${response.distance_km.toFixed(1)} km`);
  console.log(`   CO2e (WTW): ${response.co2e.toFixed(2)} kg`);
  console.log(`   - TTW (direct): ${response.co2e_ttw.toFixed(2)} kg`);
  console.log(`   - WTT (upstream): ${response.co2e_wtw.toFixed(2)} kg`);
  console.log(`   Intensity: ${(response.co2e/response.distance_km).toFixed(3)} kg CO2e/km`);
  ```

  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST "https://api.dcycle.io/v1/logistics/calculate" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "x-organization-id: YOUR_ORG_ID" \
    -H "x-user-id: YOUR_USER_ID" \
    -H "Content-Type: application/json" \
    -d '{
      "origin": "Madrid, Spain",
      "destination": "Barcelona, Spain",
      "toc": "rigid_diesel",
      "load": 5000,
      "load_unit": "kg",
      "year": 2024
    }'
  ```
</CodeGroup>

### Understanding TOCs (Transport Operation Categories)

ISO 14083 defines standard vehicle categories:

| TOC                  | Description              | Typical Use               |
| -------------------- | ------------------------ | ------------------------- |
| `van_diesel`         | Light commercial vehicle | Urban deliveries, courier |
| `rigid_diesel`       | Single-unit truck        | Regional freight          |
| `articulated_diesel` | Tractor-trailer          | Long-haul freight         |
| `rail_freight`       | Freight train            | Bulk goods                |
| `maritime_container` | Container ship           | International shipping    |
| `air_cargo`          | Cargo aircraft           | Express shipments         |

Get all available TOCs:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
# List all Transport Operation Categories
tocs = requests.get(
    "https://api.dcycle.io/v1/logistics/tocs",
    headers=headers
).json()

print("Available Transport Categories:")
for toc in tocs:
    print(f"  - {toc['code']}: {toc['description']}")
    print(f"    Fuel: {toc['fuel_type']}, Category: {toc['vehicle_category']}")
```

## Step 2: Advanced Shipment Parameters

### Specify Exact Locations

For more accurate distance calculation:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
shipment = {
    "origin_address": "Calle de Alcalá, 123, 28009 Madrid",
    "origin_postal_code": "28009",
    "origin_country": "ES",
    "destination_address": "Passeig de Gràcia, 45, 08007 Barcelona",
    "destination_postal_code": "08007",
    "destination_country": "ES",
    "toc": "rigid_diesel",
    "load": 5000,
    "load_unit": "kg",
    "year": 2024
}

response = requests.post(url, headers=headers, json=shipment).json()
```

### Multi-Client Logistics Operations

Track shipments by client:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
# First, create clients
client = {
    "name": "Client A - Retail Co",
    "contact_email": "logistics@clienta.com",
    "client_code": "CLIENT-A-001"
}

client_response = requests.post(
    "https://api.dcycle.io/v1/logistics/clients",
    headers=headers,
    json=client
).json()

client_id = client_response['id']

# Then associate shipments with clients
shipment = {
    "origin": "Warehouse Madrid",
    "destination": "Client A Store - Barcelona",
    "client_id": client_id,  # Link to client
    "toc": "van_diesel",
    "load": 1200,
    "load_unit": "kg",
    "year": 2024
}

response = requests.post(url, headers=headers, json=shipment).json()
```

### Shipment Metadata

Add custom fields for reporting:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
shipment = {
    "origin": "Madrid",
    "destination": "Barcelona",
    "toc": "rigid_diesel",
    "load": 5000,
    "load_unit": "kg",
    "year": 2024,
    # Optional metadata
    "shipment_reference": "SHP-2024-00123",
    "order_number": "ORD-45678",
    "delivery_date": "2024-03-15",
    "driver_name": "John Doe",
    "notes": "Express delivery, fragile items"
}
```

## Step 3: Bulk Upload Shipments

For high-volume operations, use CSV bulk upload:

### Prepare CSV File

```csv theme={"theme":{"light":"github-light","dark":"github-dark"}}
origin,destination,toc,load,load_unit,year,client_id,shipment_reference
"Madrid, Spain","Barcelona, Spain",rigid_diesel,5000,kg,2024,client-uuid-1,SHP-001
"Barcelona, Spain","Valencia, Spain",van_diesel,1200,kg,2024,client-uuid-1,SHP-002
"Madrid, Spain","Sevilla, Spain",articulated_diesel,12000,kg,2024,client-uuid-2,SHP-003
"Valencia, Spain","Bilbao, Spain",rigid_diesel,6500,kg,2024,client-uuid-2,SHP-004
```

### Upload and Process

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Step 1: Get presigned upload URL
upload_response = requests.post(
    "https://api.dcycle.io/v1/logistics/bulk/csv",
    headers=headers
).json()

presigned_url = upload_response['upload_url']
status_url = upload_response['status_url']

# Step 2: Upload CSV to S3
with open('shipments_march_2024.csv', 'rb') as f:
    requests.put(presigned_url, data=f)

print("✅ CSV uploaded, processing...")

# Step 3: Poll for completion
import time

while True:
    status = requests.get(status_url, headers=headers).json()

    if status['status'] == 'completed':
        print(f"✅ Processed {status['records_processed']} shipments")
        print(f"   Total CO2e: {status['total_co2e']:.2f} kg")
        print(f"   Total distance: {status['total_distance_km']:.1f} km")
        break
    elif status['status'] == 'failed':
        print(f"❌ Upload failed: {status['error']}")
        break
    elif status['status'] == 'processing':
        progress = status.get('progress_percentage', 0)
        print(f"⏳ Processing... {progress}%")

    time.sleep(5)
```

### CSV Format Requirements

**Required fields**:

* `origin`: Address or city name
* `destination`: Address or city name
* `toc`: Transport Operation Category code
* `load`: Cargo weight
* `load_unit`: Unit (kg, ton, lb)
* `year`: Calculation year

**Optional fields**:

* `client_id`: Client UUID (from `/api/v1/logistics/clients`)
* `origin_country`: ISO country code (ES, FR, etc.)
* `destination_country`: ISO country code
* `shipment_reference`: Your internal reference
* `order_number`: Order/invoice number
* `delivery_date`: YYYY-MM-DD format

## Step 4: Query and Analyze

### Get Client Report

Generate emissions report by client:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
client_id = "client-uuid-here"

report = requests.get(
    f"https://api.dcycle.io/v1/logistics/clients/{client_id}/report",
    headers=headers,
    params={
        "start_date": "2024-01-01",
        "end_date": "2024-03-31",
        "page": 1,
        "size": 100
    }
).json()

print(f"📊 Client Report: {report['client_name']}")
print(f"   Period: {report['start_date']} to {report['end_date']}")
print(f"   Total shipments: {report['total_shipments']}")
print(f"   Total distance: {report['total_distance_km']:,.1f} km")
print(f"   Total CO2e: {report['total_co2e']:,.2f} kg")
print(f"   Average per shipment: {report['average_co2e_per_shipment']:.2f} kg")
```

### List All Clients

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
clients = requests.get(
    "https://api.dcycle.io/v1/logistics/clients",
    headers=headers,
    params={"page": 1, "size": 50}
).json()

print("📋 Logistics Clients:")
for client in clients['items']:
    print(f"   - {client['name']} ({client['client_code']})")
    print(f"     Shipments: {client['total_shipments']}")
    print(f"     Total CO2e: {client['total_co2e']:.2f} kg")
```

### Generate Period Report

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Get organization-wide logistics report
report = requests.get(
    "https://api.dcycle.io/v1/logistics/report",
    headers=headers,
    params={
        "start_date": "2024-01-01",
        "end_date": "2024-12-31"
    }
).json()

print(f"📊 Annual Logistics Report 2024")
print(f"   Total shipments: {report['total_shipments']:,}")
print(f"   Total distance: {report['total_distance_km']:,.0f} km")
print(f"   Total CO2e: {report['total_co2e']:,.1f} kg ({report['total_co2e']/1000:.2f} tons)")
print(f"\nBreakdown by transport mode:")

for mode in report['by_transport_mode']:
    print(f"   - {mode['toc']}: {mode['co2e']:.1f} kg CO2e ({mode['percentage']:.1f}%)")
```

## Real-World Example: Logistics Management System

Complete workflow for managing transport emissions:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import requests
import os
from datetime import date, datetime
import csv

class LogisticsManager:
    def __init__(self):
        self.headers = {
            "Authorization": f"Bearer {os.getenv('DCYCLE_API_KEY')}",
            "x-organization-id": os.getenv("DCYCLE_ORG_ID"),
            "x-user-id": os.getenv("DCYCLE_USER_ID")
        }
        self.base_url = "https://api.dcycle.io"

    def create_client(self, name, email, code):
        """Create new logistics client"""
        response = requests.post(
            f"{self.base_url}/api/v1/logistics/clients",
            headers=self.headers,
            json={
                "name": name,
                "contact_email": email,
                "client_code": code
            }
        )
        return response.json()

    def calculate_shipment(self, **kwargs):
        """Calculate single shipment emissions"""
        response = requests.post(
            f"{self.base_url}/api/v1/logistics/calculate",
            headers=self.headers,
            json=kwargs
        )
        return response.json()

    def bulk_upload_shipments(self, csv_file_path):
        """Upload bulk shipments from CSV"""
        # Get presigned URL
        upload_response = requests.post(
            f"{self.base_url}/api/v1/logistics/bulk/csv",
            headers=self.headers
        ).json()

        # Upload CSV
        with open(csv_file_path, 'rb') as f:
            requests.put(upload_response['upload_url'], data=f)

        return upload_response['status_url']

    def wait_for_upload(self, status_url):
        """Wait for bulk upload to complete"""
        import time

        while True:
            status = requests.get(status_url, headers=self.headers).json()

            if status['status'] == 'completed':
                return status
            elif status['status'] == 'failed':
                raise Exception(f"Upload failed: {status.get('error')}")

            time.sleep(5)

    def get_client_report(self, client_id, start_date, end_date):
        """Generate client emissions report"""
        response = requests.get(
            f"{self.base_url}/api/v1/logistics/clients/{client_id}/report",
            headers=self.headers,
            params={
                "start_date": start_date.isoformat(),
                "end_date": end_date.isoformat(),
                "page": 1,
                "size": 1000
            }
        )
        return response.json()

    def export_monthly_report(self, year, month, output_file):
        """Export monthly emissions to CSV"""
        from calendar import monthrange

        start_date = date(year, month, 1)
        last_day = monthrange(year, month)[1]
        end_date = date(year, month, last_day)

        # Get all clients
        clients_response = requests.get(
            f"{self.base_url}/api/v1/logistics/clients",
            headers=self.headers,
            params={"page": 1, "size": 100}
        ).json()

        # Generate report for each client
        with open(output_file, 'w', newline='') as f:
            writer = csv.writer(f)
            writer.writerow([
                'Client', 'Client Code', 'Shipments',
                'Distance (km)', 'CO2e (kg)', 'Avg CO2e/Shipment'
            ])

            for client in clients_response['items']:
                report = self.get_client_report(
                    client['id'],
                    start_date,
                    end_date
                )

                if report['total_shipments'] > 0:
                    writer.writerow([
                        client['name'],
                        client['client_code'],
                        report['total_shipments'],
                        f"{report['total_distance_km']:.1f}",
                        f"{report['total_co2e']:.2f}",
                        f"{report['average_co2e_per_shipment']:.2f}"
                    ])

# Usage Example
manager = LogisticsManager()

# 1. Create client
print("Creating client...")
client = manager.create_client(
    name="RetailCo Spain",
    email="logistics@retailco.es",
    code="RET-ES-001"
)
print(f"✅ Client created: {client['id']}")

# 2. Calculate single shipment
print("\nCalculating shipment...")
shipment = manager.calculate_shipment(
    origin="Madrid, Spain",
    destination="Barcelona, Spain",
    client_id=client['id'],
    toc="rigid_diesel",
    load=5000,
    load_unit="kg",
    year=2024,
    shipment_reference="SHP-2024-00001"
)
print(f"✅ Shipment: {shipment['distance_km']:.1f} km, {shipment['co2e']:.2f} kg CO2e")

# 3. Bulk upload
print("\nUploading bulk shipments...")
status_url = manager.bulk_upload_shipments("march_shipments.csv")
result = manager.wait_for_upload(status_url)
print(f"✅ Processed {result['records_processed']} shipments")
print(f"   Total CO2e: {result['total_co2e']:,.2f} kg")

# 4. Generate monthly report
print("\nGenerating monthly report...")
manager.export_monthly_report(2024, 3, "march_2024_report.csv")
print("✅ Report exported to march_2024_report.csv")
```

## Best Practices

### 1. Choose Appropriate TOC

Select the most accurate transport category:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
def select_toc(vehicle_type, weight_kg, fuel_type="diesel"):
    """Helper to select appropriate TOC"""

    if vehicle_type == "van":
        return f"van_{fuel_type}"
    elif vehicle_type == "truck":
        if weight_kg < 7500:
            return f"rigid_{fuel_type}"
        else:
            return f"articulated_{fuel_type}"
    elif vehicle_type == "rail":
        return "rail_freight"
    elif vehicle_type == "ship":
        return "maritime_container"
    elif vehicle_type == "plane":
        return "air_cargo"

    # Default
    return f"rigid_{fuel_type}"
```

### 2. Validate Addresses

Ensure accurate geocoding:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
def validate_address(address):
    """Basic address validation"""

    # Check minimum length
    if len(address) < 5:
        return False, "Address too short"

    # Should contain city and country
    if ',' not in address:
        return False, "Include city and country (e.g., 'Madrid, Spain')"

    return True, "OK"

# Use before calculation
is_valid, message = validate_address(shipment['origin'])
if not is_valid:
    print(f"❌ Invalid origin: {message}")
```

### 3. Handle Errors Gracefully

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
def calculate_with_fallback(shipment_data):
    """Calculate with fallback to manual distance"""

    try:
        response = requests.post(url, headers=headers, json=shipment_data)
        response.raise_for_status()
        return response.json()

    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 422:  # Validation error
            # Try with manual distance
            if 'manual_distance_km' in shipment_data:
                print("⚠️  Geocoding failed, using manual distance")
                return calculate_with_manual_distance(shipment_data)
        raise
```

### 4. Track Data Quality

Monitor calculation accuracy:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
def check_distance_plausibility(origin, destination, calculated_km):
    """Sanity check calculated distance"""

    # Check for very short distances
    if calculated_km < 5:
        print(f"⚠️  Very short distance: {calculated_km} km")
        return False

    # Check for impossibly long distances
    if calculated_km > 20000:  # Half Earth circumference
        print(f"⚠️  Implausibly long distance: {calculated_km} km")
        return False

    return True
```

## Troubleshooting

### Issue: Zero Emissions Calculated

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Check response for errors
if response['co2e'] == 0:
    print("❌ Zero emissions calculated")
    print(f"   TOC: {response.get('toc')}")
    print(f"   Distance: {response.get('distance_km')} km")
    print(f"   Load: {response.get('load')} {response.get('load_unit')}")

    # Common causes:
    # 1. Invalid TOC code
    # 2. Zero load
    # 3. Zero distance (same origin/destination)
```

### Issue: Geocoding Failed

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Provide manual distance as fallback
shipment = {
    "origin": "Warehouse A",
    "destination": "Customer Site B",
    "manual_distance_km": 450,  # Fallback distance
    "toc": "rigid_diesel",
    "load": 5000,
    "load_unit": "kg",
    "year": 2024
}

# Or use more specific addresses
shipment = {
    "origin_address": "Calle Example, 123",
    "origin_postal_code": "28001",
    "origin_city": "Madrid",
    "origin_country": "ES",
    # ...
}
```

### Issue: Bulk Upload Fails

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
# Check CSV format
def validate_csv(file_path):
    """Validate CSV before upload"""

    required_fields = ['origin', 'destination', 'toc', 'load', 'load_unit', 'year']

    with open(file_path, 'r') as f:
        reader = csv.DictReader(f)
        headers = reader.fieldnames

        # Check required fields
        missing = [f for f in required_fields if f not in headers]
        if missing:
            print(f"❌ Missing required fields: {missing}")
            return False

        # Check data
        for i, row in enumerate(reader, start=2):
            # Check load is numeric
            try:
                float(row['load'])
            except ValueError:
                print(f"❌ Row {i}: Invalid load value '{row['load']}'")
                return False

        return True

# Validate before upload
if validate_csv('shipments.csv'):
    status_url = manager.bulk_upload_shipments('shipments.csv')
```

## Next Steps

<CardGroup cols={2}>
  <Card title="New API: Create Request" icon="plus" href="/api-reference/logistics/create-request">
    New simplified endpoint for single shipments
  </Card>

  <Card title="New API: Bulk Requests" icon="layer-group" href="/api-reference/logistics/create-requests-bulk">
    Process up to 5,000 shipments per request
  </Card>

  <Card title="GHG Protocol Tutorial" icon="car" href="/guides/emissions/ghg-protocol-tutorial#calculating-mobile-combustion">
    Track company vehicle emissions
  </Card>

  <Card title="Core Concepts" icon="book" href="/docs/core-concepts">
    Learn about ISO 14083 standard
  </Card>
</CardGroup>
