curl -X POST "https://api.dcycle.io/v2/business-travels" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 3e2d1c0b-9a8f-4e7d-a6c5-b4a392817161" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"transport_type": "train",
"fuel_type": "electric",
"renewable_energy": "no",
"origin": "Madrid",
"destination": "Barcelona",
"travel_type": "round",
"start_date": "2026-03-04",
"email": "ana@example.com",
"client_row_id": "TRIP-2026-0311"
},
{
"transport_type": "car",
"vehicle_size": "medium",
"fuel_type": "diesel",
"distance_km": 42,
"travel_number": 20,
"start_date": "2026-03-01",
"end_date": "2026-03-31"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/business-travels",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"transport_type": "aircraft",
"origin": "Madrid",
"destination": "London",
"travel_type": "round",
"start_date": "2026-03-04",
"client_row_id": "TRIP-2026-0312",
}
]
},
timeout=60,
)
response.raise_for_status()
job = response.json()
print(job["id"], job["status"])
{
"id": "4c3b2a19-0817-4f6e-9d5c-4b3a29181706",
"status": "processing",
"entity_type": "business_travels",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-20T09:12:44.031Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/4c3b2a19-0817-4f6e-9d5c-4b3a29181706",
"items": "/v2/ingest-jobs/4c3b2a19-0817-4f6e-9d5c-4b3a29181706/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 0,
"client_row_id": "TRIP-2026-0311",
"error_code": "FUEL_NOT_ALLOWED_FOR_TRANSPORT",
"error_params": { "field": "fuel_type", "value": "petrol" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
Create Business Travels (v2)
Create up to 5,000 business travels in one request; distances and emissions are calculated asynchronously
POST
/
v2
/
business-travels
curl -X POST "https://api.dcycle.io/v2/business-travels" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 3e2d1c0b-9a8f-4e7d-a6c5-b4a392817161" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"transport_type": "train",
"fuel_type": "electric",
"renewable_energy": "no",
"origin": "Madrid",
"destination": "Barcelona",
"travel_type": "round",
"start_date": "2026-03-04",
"email": "ana@example.com",
"client_row_id": "TRIP-2026-0311"
},
{
"transport_type": "car",
"vehicle_size": "medium",
"fuel_type": "diesel",
"distance_km": 42,
"travel_number": 20,
"start_date": "2026-03-01",
"end_date": "2026-03-31"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/business-travels",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"transport_type": "aircraft",
"origin": "Madrid",
"destination": "London",
"travel_type": "round",
"start_date": "2026-03-04",
"client_row_id": "TRIP-2026-0312",
}
]
},
timeout=60,
)
response.raise_for_status()
job = response.json()
print(job["id"], job["status"])
{
"id": "4c3b2a19-0817-4f6e-9d5c-4b3a29181706",
"status": "processing",
"entity_type": "business_travels",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-20T09:12:44.031Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/4c3b2a19-0817-4f6e-9d5c-4b3a29181706",
"items": "/v2/ingest-jobs/4c3b2a19-0817-4f6e-9d5c-4b3a29181706/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 0,
"client_row_id": "TRIP-2026-0311",
"error_code": "FUEL_NOT_ALLOWED_FOR_TRANSPORT",
"error_params": { "field": "fuel_type", "value": "petrol" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
← Business Travels API
Create one or many business travels (Scope 3 category 6). Say how each trip was made, when, how often and how far, or
between which places; Dcycle works out the distance when you leave it out, picks the emission factor and calculates
the emissions in the background. The travels are the ones
Request-level errors:
/v1/business-travels
reads and edits.
The request checks every record and writes them all at once, then answers 202 Accepted with an
ingest job that calculates them. Follow it with
GET /v2/ingest-jobs/{id} or the
ingest_job.finished webhook.
Beta. Part of the bulk ingest API, currently in beta. The contract may still change before general availability.
curl -X POST "https://api.dcycle.io/v2/business-travels" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 3e2d1c0b-9a8f-4e7d-a6c5-b4a392817161" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"transport_type": "train",
"fuel_type": "electric",
"renewable_energy": "no",
"origin": "Madrid",
"destination": "Barcelona",
"travel_type": "round",
"start_date": "2026-03-04",
"email": "ana@example.com",
"client_row_id": "TRIP-2026-0311"
},
{
"transport_type": "car",
"vehicle_size": "medium",
"fuel_type": "diesel",
"distance_km": 42,
"travel_number": 20,
"start_date": "2026-03-01",
"end_date": "2026-03-31"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/business-travels",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"transport_type": "aircraft",
"origin": "Madrid",
"destination": "London",
"travel_type": "round",
"start_date": "2026-03-04",
"client_row_id": "TRIP-2026-0312",
}
]
},
timeout=60,
)
response.raise_for_status()
job = response.json()
print(job["id"], job["status"])
{
"id": "4c3b2a19-0817-4f6e-9d5c-4b3a29181706",
"status": "processing",
"entity_type": "business_travels",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-20T09:12:44.031Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/4c3b2a19-0817-4f6e-9d5c-4b3a29181706",
"items": "/v2/ingest-jobs/4c3b2a19-0817-4f6e-9d5c-4b3a29181706/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 0,
"client_row_id": "TRIP-2026-0311",
"error_code": "FUEL_NOT_ALLOWED_FOR_TRANSPORT",
"error_params": { "field": "fuel_type", "value": "petrol" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
How it works
- Every record is checked. If any is invalid the whole request is rejected with
422and nothing is written; the answer lists every invalid record, so you can fix them all at once. - The travels are written at once, with status
pending, in thex-organization-idorganization. - The job calculates them in the background, in chunks. A travel sent without
distance_kmgets the distance between its origin and destination for its transport (road or rail route, great-circle for a flight, sea route for a ferry), and its places are located. Then the emission factor of its transport, fuel and country is applied. Each travel endsactiveorerror. Its job item is thenfailedwith the reason when there is one:TRANSPORT_MODE_INVALID_FOR_ROUTE: there is no route for that transport between the two places (e.g. a car between continents).DISTANCE_CALCULATION_ERRORorGEOCODE_LOCATION_ERROR: a place could not be found.CALCULATION_FAILED: no emission factor for the trip, or the distance provider was unavailable (send the travel again later, or withdistance_km).
- When every chunk is done the job is
completed(orcompleted_with_errors) andingest_job.finishedis sent to your webhook endpoints.
How a trip is counted
A record is a trip madetravel_number times in its period: its emissions are those of distance_km (twice for
travel_type: round) for each trip, times passenger_number. A trip over several days is one record with its
start_date and end_date; recurring trips (a commute to a client) are one record with their count.
Request
Headers
string
required
Your API key (see Authentication).
string
required
The organization the travels belong to. A record cannot name another organization: send each organization’s
travels with its own
x-organization-id.string
required
1 to 255 characters, unique per batch. A retry with the same key and body returns the original job
(with
Idempotent-Replayed: true) instead of creating the travels again; the same key with another body is 422 IDEMPOTENCY_KEY_REUSED, and while the first request is still being processed
409 IDEMPOTENCY_KEY_IN_PROGRESS.Query Parameters
boolean
default:"false"
Check only: the same
422 for invalid records, nothing written, no job; 200 with
{"dry_run": true, "submitted": n, "valid": n} when every record is valid.Body Parameters
array[object]
required
The business travels to create, 1 to 5,000. A field not listed here is rejected (
SCHEMA_INVALID).Show record
Show record
string
required
car, motorbike, bus, trolleybus, metro, train, aircraft or ferry.date
required
Day of the trip, or first day of the period it repeats in,
YYYY-MM-DD, from 1970, not in the future.date
Last day of the period, on or after
start_date and at most 366 days in, not in the future. Omitted: the trip
is a one-day record.number
One-way distance of one trip, in km, over 0. Omitted: calculated by the job from
origin and destination.string
Where the trip starts: an address, a city, an airport, or
lat,lng, up to 500 characters. Send it with
destination when there is no distance_km. When it is a place, its country picks the emission factor’s
country.string
Where it ends, as
origin. With no distance_km it must be another place than origin.string
default:"one_way"
one_way, or round for there and back (twice the distance).integer
default:"1"
How many times the trip was made in the period: 1 to 100 a day of the period, and 10,000 at most.
integer
default:"1"
People travelling together, 1 to 100.
string
For a
car, and only for a car (required): small, medium or large.string
What the vehicle runs on:
diesel, natural_gas, petrol, lpg, electric, hybrid or do_not_know.
Required except for aircraft and ferry, which take none. Only the fuels the transport is calculated with are
accepted (FUEL_NOT_ALLOWED_FOR_TRANSPORT otherwise):transport_type | fuel_type |
|---|---|
car | diesel, natural_gas, petrol, lpg, electric, hybrid, do_not_know |
motorbike | diesel, petrol, electric, do_not_know |
bus | diesel, natural_gas, petrol, lpg, do_not_know |
train | diesel, electric, do_not_know |
metro, trolleybus | electric, do_not_know |
string
With
fuel_type: electric, and only with it (required): yes when the electricity is renewable, else no.string
A label for the trip, up to 255 characters.
string
Who travelled, e.g. their work email, up to 255 characters.
uuid
A record of your custom emission factor databases to
calculate with instead of the default: find it with List Records.
Its
activity_categories must include travels.string
Your own reference for the record, up to 255 characters, echoed in errors and in the job’s items.
The file import template writes some of these values differently:
car_small is
transport_type: car + vehicle_size: small here, return_trip is travel_type: round, and
electric_renewable / electric_not_renewable is fuel_type: electric + renewable_energy: yes / no.Response
202 Accepted with the job (entity_type: business_travels, operation: create) and a Location header to it. To
read the result:
GET /v2/ingest-jobs/{id}/itemshas one item per record, in the order sent. Itsentity_idis the travel’s id. Afaileditem carries itserror_code.- List Business Travels returns the travels with their status, distance and
emissions (
co2e).
Errors
Problem details. Per-record codes inerrors[].error_code of a 422 BULK_RECORDS_REJECTED:
error_code | Cause |
|---|---|
SCHEMA_INVALID | The record does not match the schema: a missing or unknown field (e.g. organization_id), no distance_km and no origin + destination, the same place twice without a distance, a date in the future or out of range, a car without vehicle_size, a fuel on an aircraft or ferry, no fuel on another transport, renewable_energy without electric, more than 100 passengers… error_params.errors says where. |
FUEL_NOT_ALLOWED_FOR_TRANSPORT | fuel_type is not one the transport is calculated with (see the table above). |
MULTIDB_CUSTOM_RECORD_NOT_FOUND_OR_DISABLED | custom_ef_record_id does not exist, is disabled or is not visible from your organization. |
MULTIDB_CUSTOM_RECORD_INCOMPATIBLE_CATEGORY | custom_ef_record_id is not configured for the travels activity category. |
401 for a missing or invalid API key, 403 LOGGED_USER_NOT_MEMBER when the key’s user is not a
member of the organization, 422 REQUEST_VALIDATION_FAILED for a malformed body (no records, more than 5,000, no
Idempotency-Key), 422 IDEMPOTENCY_KEY_REUSED, 409 IDEMPOTENCY_KEY_IN_PROGRESS, and 429 beyond the
rate limits.
Limits
| Limit | Value |
|---|---|
| Records per request | 1 to 5,000 |
| Travels per calculation chunk | 100 |
Related Endpoints
Delete Business Travels (v2)
Delete travels by id
Get Ingest Job
Follow the calculation
List Job Items
The travel id and outcome of each record
List Business Travels
The travels and their emissions
Was this page helpful?