Upload CSV
const options = {
method: 'POST',
headers: {
'x-api-key': '<x-api-key>',
'x-organization-id': '<x-organization-id>',
'x-user-id': '<x-user-id>',
'Content-Type': 'application/json'
},
body: JSON.stringify({requests_file_name: '<string>', recharges_file_name: '<string>'})
};
fetch('https://api.dcycle.io/api/v1/logistics/presigned_url', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.dcycle.io/api/v1/logistics/presigned_url"
payload = {
"requests_file_name": "<string>",
"recharges_file_name": "<string>"
}
headers = {
"x-api-key": "<x-api-key>",
"x-organization-id": "<x-organization-id>",
"x-user-id": "<x-user-id>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)curl --request POST \
--url https://api.dcycle.io/api/v1/logistics/presigned_url \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--header 'x-organization-id: <x-organization-id>' \
--header 'x-user-id: <x-user-id>' \
--data '
{
"requests_file_name": "<string>",
"recharges_file_name": "<string>"
}
'{
"requests.upload_url": "<string>",
"requests.file_id": "<string>",
"requests.expires_at": "<string>"
}Upload CSV
Get a presigned URL to upload bulk shipments via CSV
POST
/
api
/
v1
/
logistics
/
presigned_url
Upload CSV
const options = {
method: 'POST',
headers: {
'x-api-key': '<x-api-key>',
'x-organization-id': '<x-organization-id>',
'x-user-id': '<x-user-id>',
'Content-Type': 'application/json'
},
body: JSON.stringify({requests_file_name: '<string>', recharges_file_name: '<string>'})
};
fetch('https://api.dcycle.io/api/v1/logistics/presigned_url', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.dcycle.io/api/v1/logistics/presigned_url"
payload = {
"requests_file_name": "<string>",
"recharges_file_name": "<string>"
}
headers = {
"x-api-key": "<x-api-key>",
"x-organization-id": "<x-organization-id>",
"x-user-id": "<x-user-id>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)curl --request POST \
--url https://api.dcycle.io/api/v1/logistics/presigned_url \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--header 'x-organization-id: <x-organization-id>' \
--header 'x-user-id: <x-user-id>' \
--data '
{
"requests_file_name": "<string>",
"recharges_file_name": "<string>"
}
'{
"requests.upload_url": "<string>",
"requests.file_id": "<string>",
"requests.expires_at": "<string>"
}Bulk Shipment Upload
Upload thousands of shipments at once via a CSV file. This endpoint returns a presigned S3 URL so you can upload your file, which will be processed asynchronously.This method is recommended for bulk uploads. Shipments are persisted in the database and available for subsequent reports.
Upload Flow
1
Request presigned URL
Make a POST request to
/api/v1/logistics/presigned_url with your file name2
Upload to S3
Use the presigned URL to upload your CSV directly to S3 (PUT request)
3
Asynchronous processing
The system processes your file in the background and calculates emissions
4
Verify results
Check your clients and reports with the listing endpoints
Step 1: Request Presigned URL
Request
Headers
string
required
Your API key for authenticationExample:
sk_live_1234567890abcdefstring
required
Your organization UUIDExample:
ff4adcc7-8172-45fe-9cf1-e90a6de53aa9string
required
Your user UUIDExample:
a1b2c3d4-e5f6-7890-abcd-ef1234567890Body Parameters
string
required
Name of the CSV file you’re going to uploadExample:
"correos_shipments_2024-11.csv"string
Optional: Name of the recharges/returns CSV fileExample:
"correos_returns_2024-11.csv"Response
string
Presigned S3 URL to upload the shipments file
string
File UUID (use it for tracking)
string
Timestamp when the URL expires (15 minutes)
Example
curl -X POST "https://api.dcycle.io/api/v1/logistics/presigned_url" \
-H "Authorization: Bearer ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "x-user-id: ${DCYCLE_USER_ID}" \
-H "Content-Type: application/json" \
-d '{
"requests_file_name": "shipments_2024-11.csv"
}'
import requests
import os
api_key = os.getenv("DCYCLE_API_KEY")
org_id = os.getenv("DCYCLE_ORG_ID")
headers = {
"Authorization": f"Bearer {api_key}",
"x-organization-id": org_id,
"x-user-id": user_id,
"Content-Type": "application/json"
}
payload = {
"requests_file_name": "shipments_2024-11.csv"
}
response = requests.post(
"https://api.dcycle.io/api/v1/logistics/presigned_url",
headers=headers,
json=payload
)
result = response.json()
upload_url = result["requests"]["upload_url"]
file_id = result["requests"]["file_id"]
print(f"Upload URL obtained: {upload_url[:50]}...")
print(f"File ID: {file_id}")
const axios = require('axios');
const apiKey = process.env.DCYCLE_API_KEY;
const orgId = process.env.DCYCLE_ORG_ID;
const headers = {
'Authorization': `Bearer ${apiKey}`,
'x-organization-id': orgId,
'x-user-id': userId,
'Content-Type': 'application/json'
};
const payload = {
requests_file_name: "shipments_2024-11.csv"
};
axios.post(
'https://api.dcycle.io/api/v1/logistics/presigned_url',
payload,
{ headers }
)
.then(response => {
const { upload_url, file_id } = response.data.requests;
console.log('Upload URL:', upload_url.substring(0, 50) + '...');
console.log('File ID:', file_id);
})
.catch(error => console.error(error));
Successful Response
{
"requests": {
"upload_url": "https://dcycle-logistics.s3.amazonaws.com/uploads/...",
"file_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"expires_at": "2024-11-20T15:45:00Z"
}
}
Step 2: Upload CSV to S3
Use the presigned URL to upload your CSV file directly to S3:# The presigned URL already includes authentication parameters
curl -X PUT "${UPLOAD_URL}" \
-H "Content-Type: text/csv" \
--data-binary @shipments_2024-11.csv
import requests
# Read CSV file
with open('shipments_2024-11.csv', 'rb') as file:
csv_data = file.read()
# Upload to S3
response = requests.put(
upload_url,
data=csv_data,
headers={'Content-Type': 'text/csv'}
)
if response.status_code == 200:
print("✅ File uploaded successfully")
else:
print(f"❌ Error: {response.status_code}")
const fs = require('fs');
const axios = require('axios');
// Read CSV file
const csvData = fs.readFileSync('shipments_2024-11.csv');
// Upload to S3
axios.put(uploadUrl, csvData, {
headers: { 'Content-Type': 'text/csv' }
})
.then(() => console.log('✅ File uploaded successfully'))
.catch(error => console.error('❌ Error:', error));
The presigned URL expires in 15 minutes. Make sure to upload the file within that time.
CSV Format
The CSV must follow this structure:client,origin,destination,toc,load,load_unit,date,reference
Correos Express,Madrid,Barcelona,van_diesel,1000,kg,2024-11-20,REF-001
Correos Express,Barcelona,Valencia,truck_diesel,2500,kg,2024-11-20,REF-002
Correos Express,Madrid,Sevilla,van_electric,800,kg,2024-11-21,REF-003
Required Columns
| Column | Description | Example |
|---|---|---|
client | Client name | "Correos Express" |
origin | Origin city and country | "Madrid, Spain" |
destination | Destination city and country | "Barcelona, Spain" |
toc | Transport type | "van_diesel" |
load | Transported quantity | 1000 |
load_unit | Unit of measurement | "kg" |
date | Shipment date | "2024-11-20" |
reference | Shipment reference | "REF-001" |
Download Template
Download CSV Template
Template with correct format and sample data
Step 3: Asynchronous Processing
Once the CSV is uploaded:- Validation: The system validates the format and data
- Processing: Each row is processed and emissions are calculated
- Storage: Shipments are saved in the database
- Notification: (Coming soon) You’ll receive a webhook when complete
Processing can take from a few seconds to several minutes depending on file size.
Step 4: Verify Results
After processing, you can query your data:# List clients
curl "https://api.dcycle.io/api/v1/logistics/clients" \
-H "Authorization: Bearer ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "x-user-id: ${DCYCLE_USER_ID}"
# Get report
curl "https://api.dcycle.io/api/v1/logistics/clients/report?client=Correos+Express&start_date=2024-11-01&end_date=2024-11-30" \
-H "Authorization: Bearer ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "x-user-id: ${DCYCLE_USER_ID}"
Complete Example Script
import requests
import os
from time import sleep
class DcycleLogistics:
def __init__(self, api_key, org_id):
self.api_key = api_key
self.org_id = org_id
self.base_url = "https://api.dcycle.io"
self.headers = {
"Authorization": f"Bearer {api_key}",
"x-organization-id": org_id,
"x-user-id": user_id,
"Content-Type": "application/json"
}
def upload_csv(self, csv_file_path):
"""Complete CSV upload: presigned URL + upload + verification"""
# 1. Get presigned URL
file_name = os.path.basename(csv_file_path)
response = requests.post(
f"{self.base_url}/api/v1/logistics/presigned_url",
headers=self.headers,
json={"requests_file_name": file_name}
)
response.raise_for_status()
upload_url = response.json()["requests"]["upload_url"]
file_id = response.json()["requests"]["file_id"]
print(f"✅ Presigned URL obtained (File ID: {file_id})")
# 2. Upload file
with open(csv_file_path, 'rb') as f:
upload_response = requests.put(
upload_url,
data=f,
headers={'Content-Type': 'text/csv'}
)
upload_response.raise_for_status()
print(f"✅ File uploaded successfully")
# 3. Wait for processing (optional)
print("⏳ Processing... (this may take a few minutes)")
sleep(30) # Wait 30 seconds
# 4. Verify clients
clients = self.get_clients()
print(f"✅ Available clients: {', '.join(clients)}")
return file_id
def get_clients(self):
"""List logistics clients"""
response = requests.get(
f"{self.base_url}/api/v1/logistics/clients",
headers=self.headers
)
response.raise_for_status()
return response.json()
# Usage
if __name__ == "__main__":
client = DcycleLogistics(
api_key=os.getenv("DCYCLE_API_KEY"),
org_id=os.getenv("DCYCLE_ORG_ID")
)
client.upload_csv("shipments_2024-11.csv")
#!/bin/bash
# Variables
API_KEY="${DCYCLE_API_KEY}"
ORG_ID="${DCYCLE_ORG_ID}"
CSV_FILE="shipments_2024-11.csv"
BASE_URL="https://api.dcycle.io"
# 1. Get presigned URL
echo "📤 Requesting presigned URL..."
RESPONSE=$(curl -s -X POST "${BASE_URL}/api/v1/logistics/presigned_url" \
-H "Authorization: Bearer ${API_KEY}" \
-H "x-organization-id: ${ORG_ID}" \
-H "x-user-id: ${DCYCLE_USER_ID}" \
-H "Content-Type: application/json" \
-d "{\"requests_file_name\": \"${CSV_FILE}\"}")
UPLOAD_URL=$(echo $RESPONSE | jq -r '.requests.upload_url')
FILE_ID=$(echo $RESPONSE | jq -r '.requests.file_id')
echo "✅ URL obtained (File ID: ${FILE_ID})"
# 2. Upload file
echo "📤 Uploading CSV file..."
curl -X PUT "${UPLOAD_URL}" \
-H "Content-Type: text/csv" \
--data-binary @"${CSV_FILE}"
echo "✅ File uploaded successfully"
echo "⏳ File is being processed..."
Common Errors
400 Bad Request - Missing file_name
Cause: requests_file_name is required{
"detail": "requests_file_name is required"
}
requests_file_name parameter in the body.
403 Forbidden - URL Expired
Cause: The presigned URL expired (15 minutes) Solution: Request a new presigned URL and upload the file immediately.422 Validation Error - CSV Format
Cause: The CSV has incorrect format or missing columns Solution: Verify that your CSV has all required columns and correct format.Limits and Recommendations
| Limit | Value |
|---|---|
| Maximum file size | 100 MB |
| Maximum rows per CSV | 50,000 |
| URL expiration time | 15 minutes |
| Processing time | ~1 second per 100 rows |
For very large files (>50k rows), consider splitting them into multiple smaller CSVs.
Related Endpoints
Get Clients
List your logistics clients
Get Report
Generate ISO 14083 report
Calculate Shipment
Individual calculation without persistence
Get TOCs
List transport types
Was this page helpful?