Skip to main content
POST
/
v1
/
transports
/
upload
Upload Transport File
const options = {
  method: 'POST',
  headers: {
    'x-api-key': '<x-api-key>',
    'x-organization-id': '<x-organization-id>',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({transport_direction: '<string>'})
};

fetch('https://api.dcycle.io/v1/transports/upload', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
import requests

url = "https://api.dcycle.io/v1/transports/upload"

payload = { "transport_direction": "<string>" }
headers = {
    "x-api-key": "<x-api-key>",
    "x-organization-id": "<x-organization-id>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
curl --request POST \
  --url https://api.dcycle.io/v1/transports/upload \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <x-api-key>' \
  --header 'x-organization-id: <x-organization-id>' \
  --data '
{
  "transport_direction": "<string>"
}
'
{
  "message": "<string>",
  "file_id": "<string>"
}

Upload Transport File

Upload a spreadsheet file containing transport routes. The file is processed asynchronously — rows are parsed, geocoded, and emission factors are resolved in the background after the upload returns.
For files larger than a few MB, use the Presigned URL approach instead. It uploads the file directly to S3 without routing the bytes through the API server.

Request

Headers

x-api-key
string
required
Your API key for authenticationExample: sk_live_1234567890abcdef
x-organization-id
string
required
Your organization UUID.Example: a8315ef3-dd50-43f8-b7ce-d839e68d51fa

Body (multipart/form-data)

transport_direction
string
required
The GHG Protocol scope 3 direction of these transport routes.
  • downstream — transport paid by your organization after the sale (Category 9)
  • upstream — inbound transport paid by your organization (Category 4)
upload_file
file
required
The spreadsheet file to upload. Accepted formats: .csv, .xls, .xlsx.

Response

message
string
Human-readable confirmation message.Example: "Transport route file uploaded"
file_id
string
UUID that identifies this upload batch. Use it to filter routes via GET /v1/transports?file_id=<file_id> or to track processing progress.Example: "b3d7c1e2-4a5f-48d9-b6e8-1234567890ab"

Example

curl -X POST "https://api.dcycle.io/v1/transports/upload" \
  -H "x-api-key: ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -F "transport_direction=downstream" \
  -F "upload_file=@transport_routes.xlsx"
import requests
import os

api_key = os.getenv("DCYCLE_API_KEY")
org_id = os.getenv("DCYCLE_ORG_ID")

headers = {
    "x-api-key": api_key,
    "x-organization-id": org_id,
}

with open("transport_routes.xlsx", "rb") as f:
    response = requests.post(
        "https://api.dcycle.io/v1/transports/upload",
        headers=headers,
        data={"transport_direction": "downstream"},
        files={"upload_file": ("transport_routes.xlsx", f, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
    )

result = response.json()
print(f"File ID: {result['file_id']}")
print(f"Message: {result['message']}")
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');

const apiKey = process.env.DCYCLE_API_KEY;
const orgId = process.env.DCYCLE_ORG_ID;

const form = new FormData();
form.append('transport_direction', 'downstream');
form.append('upload_file', fs.createReadStream('transport_routes.xlsx'));

axios.post('https://api.dcycle.io/v1/transports/upload', form, {
  headers: {
    'x-api-key': apiKey,
    'x-organization-id': orgId,
    ...form.getHeaders(),
  },
})
.then(response => {
  const result = response.data;
  console.log(`File ID: ${result.file_id}`);
  console.log(`Message: ${result.message}`);
})
.catch(error => console.error(error));

Successful Response

{
  "message": "Transport route file uploaded",
  "file_id": "b3d7c1e2-4a5f-48d9-b6e8-1234567890ab"
}

Asynchronous Processing

The endpoint returns immediately with status 201. The actual processing happens in the background:
  1. Rows are parsed from the uploaded file.
  2. Origin and destination addresses are geocoded (Google Maps Distance Matrix API for road/rail, haversine for air, sea-distance tables for maritime).
  3. Emission factors are resolved and CO2e values are calculated.
You can poll the processing status by listing routes filtered by the returned file_id:
curl "https://api.dcycle.io/v1/transports?file_id=b3d7c1e2-4a5f-48d9-b6e8-1234567890ab" \
  -H "x-api-key: ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}"
Route statuses move through pendingactive (success) or error as processing completes.

Common Errors

401 Unauthorized

Cause: Missing or invalid API key / Bearer token.
{
  "detail": "Invalid API key",
  "code": "INVALID_API_KEY"
}

422 Unprocessable Entity

Cause: transport_direction is missing or not one of the accepted values.
{
  "detail": [
    {
      "loc": ["body", "transport_direction"],
      "msg": "value is not a valid enumeration member",
      "type": "type_error.enum"
    }
  ]
}
Solution: Set transport_direction to downstream or upstream.

400 Bad Request

Cause: Unsupported file format (not .csv, .xls, or .xlsx).
{
  "detail": "Unsupported file format. Use CSV, XLS, or XLSX.",
  "code": "INVALID_FILE_FORMAT"
}

Presigned URL Upload

Upload large files directly to S3 via a presigned URL

List Transport Routes

Retrieve transport routes, filter by file_id to track batch processing

Transport Combinations

Explore valid transport type and method combinations before creating routes

Transport Overview

Learn the full Transport API data model and workflow