Skip to main content
POST
/
v1
/
transports
/
upload
/
presigned-url
Get Presigned Upload URL
const options = {
  method: 'POST',
  headers: {'x-organization-id': '<x-organization-id>', 'Content-Type': '<content-type>'},
  body: JSON.stringify({file_name: '<string>', transport_direction: '<string>'})
};

fetch('https://api.dcycle.io/v1/transports/upload/presigned-url', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
{
  "upload_url": "<string>",
  "file_id": "<string>",
  "file_name": "<string>",
  "destination_file_key": "<string>",
  "message": "<string>"
}

Get Presigned Upload URL

Generate a pre-signed Amazon S3 URL that lets you upload a transport route file directly from the client to S3 — bypassing the API server entirely. This is the recommended approach for files larger than a few MB.

Two-Step Upload Flow

  1. Call this endpoint to receive a upload_url and a file_id.
  2. PUT the raw file bytes to upload_url. No authentication headers are needed for the S3 PUT — the signature is embedded in the URL.
Once the file lands in S3, Dcycle picks it up automatically and begins asynchronous processing (geocoding, emission factor resolution, CO2e calculation).

Request

Headers

x-api-key
string
API key for authentication. Either this header or Authorization is required.Example: sk_live_1234567890abcdef
Authorization
string
Bearer token for authentication. Either this header or x-api-key is required.Example: Bearer sk_live_1234567890abcdef
x-organization-id
string
required
Your organization UUID.Example: a8315ef3-dd50-43f8-b7ce-d839e68d51fa
Content-Type
string
required
Must be application/json.

Body (JSON)

file_name
string
required
The name of the file being uploaded. Must end with .csv, .xls, or .xlsx.Example: "q1_transport_routes.xlsx"
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)

Response

upload_url
string
Pre-signed S3 URL. Send the raw file bytes as the body of an HTTP PUT request to this URL. The URL is time-limited — use it promptly after receiving it.Example: "https://dcycle-heavy-files.s3.eu-west-1.amazonaws.com/transport-and-distribution-downstream/...?X-Amz-Signature=..."
file_id
string
UUID that identifies this upload batch. Use it to filter routes after processing via GET /v1/transports?file_id=<file_id>.Example: "c4e8f2a1-5b6d-49e0-c7f9-2345678901bc"
file_name
string
Sanitized version of the file name (alphanumeric characters only, extension preserved).Example: "q1_transport_routes"
destination_file_key
string
The S3 object key where the file will be stored. Useful for audit trails or debugging.Example: "heavy-files/transport-and-distribution-downstream/partner/org-uuid/c4e8f2a1.../user-uuid/q1_transport_routes.xlsx"
message
string
Human-readable confirmation.Example: "Upload pre-signed url generated"

Example

# Step 1: Get the presigned URL
RESPONSE=$(curl -s -X POST "https://api.dcycle.io/v1/transports/upload/presigned-url" \
  -H "x-api-key: ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -H "Content-Type: application/json" \
  -d '{
    "file_name": "q1_transport_routes.xlsx",
    "transport_direction": "downstream"
  }')

UPLOAD_URL=$(echo $RESPONSE | jq -r '.upload_url')
FILE_ID=$(echo $RESPONSE | jq -r '.file_id')

echo "File ID: $FILE_ID"

# Step 2: PUT the file directly to S3 (no auth headers needed)
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @q1_transport_routes.xlsx

Successful Response (Step 1)

{
  "upload_url": "https://dcycle-heavy-files.s3.eu-west-1.amazonaws.com/heavy-files/transport-and-distribution-downstream/dcycle/a8315ef3-dd50-43f8-b7ce-d839e68d51fa/c4e8f2a1-5b6d-49e0-c7f9-2345678901bc/550e8400-e29b-41d4-a716-446655440000/q1_transport_routes.xlsx?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=...",
  "file_id": "c4e8f2a1-5b6d-49e0-c7f9-2345678901bc",
  "file_name": "q1_transport_routes",
  "destination_file_key": "heavy-files/transport-and-distribution-downstream/dcycle/a8315ef3.../c4e8f2a1.../q1_transport_routes.xlsx",
  "message": "Upload pre-signed url generated"
}

Asynchronous Processing

After the S3 PUT completes, Dcycle picks up the file and processes it in the background:
  1. Rows are parsed from the spreadsheet.
  2. Origin and destination addresses are geocoded.
  3. Emission factors are resolved and CO2e values are calculated.
Poll for completion by listing transport routes filtered by file_id:
curl "https://api.dcycle.io/v1/transports?file_id=c4e8f2a1-5b6d-49e0-c7f9-2345678901bc" \
  -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: file_name has an unsupported extension, or transport_direction is invalid.
{
  "detail": [
    {
      "loc": ["body", "file_name"],
      "msg": "file_name must end with .csv, .xls, or .xlsx",
      "type": "value_error"
    }
  ]
}
Solution: Ensure file_name ends with .csv, .xls, or .xlsx, and that transport_direction is downstream or upstream.

S3 PUT Errors

If the S3 PUT returns a 403 Forbidden, the presigned URL may have expired. Request a new URL and retry.

Direct File Upload

Upload smaller files directly through the API (multipart/form-data)

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