π Milk Run API methodology update, vessel timeseries error handling
π Feb-Mar 2026
This release introduces a major update to the Milk Run endpoint, aligning emissions allocation with ISO 14083 methodology, together with a small improvement to error handling in the Vessel Timeseries API.
π Overview
π± Carbon emissions APIs
π Milk Run API β Methodology and behavior update
Important - behavior update: following this update, identical requests compared to previous versions may produce different total emissions. This results from the adoption of ISO 14083 allocation principles and updated terminal handling logic.
Differences reflect methodological improvements, not calculation errors.
This release introduces important improvements to the Milk Run endpoint (/asset/v2/run/co2), including ISO methodology alignment, clearer operational modeling, and enhancements to the response and validation logic.
ISO 14083 allocation method
Emission allocation now follows ISO 14083 transport activity principles: COβe emissions are allocated based on each shipmentβs transport activity (transport activity = weight Γ pickup-to-delivery distance).
This means that allocation is now independent from terminal positioning (previous allocation approach), and results are aligned with ISO 14083 methodology.
Terminal handling and loop modeling
The API no longer automatically infers a terminal between the first and last stops. Operational configurations are now explicitly supported:
- Open loop:
A β B β C - Closed loop:
A β B β C β A - Closed loop with unknown terminal:
"" β A β B β ""
When empty locations ("") are provided at the beginning and end of the sequence, an inferred terminal is introduced to model vehicle repositioning, and to account for potential empty running before the first stop and after the last stop.
This change clarifies how vehicle operations are described and removes implicit assumptions.
Location indexing update
Location indices now start at 0.
Full route visibility in responses
Responses now include all legs of the vehicle run, including now the legs connected to inferred terminals when applicable. This provides full transparency on how the route is modeled.
Empty running transparency
Empty running (distance travelled without transported shipments) is now fully transparent. It can be directly identified in the response:
- legs where
loadFactor = 0represent empty movements, - total empty running is reflected in the
emptyRunningproperty.
This improves traceability and removes ambiguity around how empty distance is accounted for.
Validation and error handling improvements
Validation logic has been revised and several small bugs have been fixed. Error messages are now more consistent with input validation rules.
Documentation
The endpoint documentation has been updated accordingly.
The detailed Milk Run methodology is now available in the Guides section of the developer documentation.
Example
Below example illustrates a closed loop (A β B β C β A) with a know terminal location (FRFOS).
This example highlights:
- ISO 14083 allocation (
allocationMethod = ISO_14083_TRANSPORT_ACTIVITY_ALLOCATION) - Allocation based on transport activity (
weight Γ pickup-to-delivery distance) - Full route visibility through returned
legs - Empty running identification via
loadFactor = 0 - Location indices starting at
0
{
"metadata": {
"MyMeta1": "Shipment-A36",
"MyMeta2": "A36-2633"
},
"locations": [
"FRFOS",
"FRMTU",
"FRMRS",
"FRSDP",
"FRFOS"
],
"orders": [
{
"fromIndex":0,
"toIndex": 2,
"weight": 1000,
"id": "ABC-001",
"type": "PARCEL"
},
{
"fromIndex": 0,
"toIndex": 2,
"weight": 5000,
"id": "ABC-002",
"type": "PARCEL"
},
{
"fromIndex": 0,
"toIndex": 3,
"weight": 1000,
"id": "ABC-003",
"type": "PARCEL"
},
{
"fromIndex": 1,
"toIndex": 3,
"weight": 1000,
"id": "ABC-004",
"type": "PARCEL"
}
],
"mode": "road",
"details": {
"truckSize": "26",
"fuel": "DIESEL"
}
}{
"metadata": {
"MyMeta1": "Shipment-A36",
"MyMeta2": "A36-2633"
},
"co2e": {
"total": 157673,
"wtt": 36275,
"ttw": 121398
},
"orders": [
{
"co2e": {
"total": 21249,
"wtt": 4889,
"ttw": 16360,
"intensity": 0.27681
},
"parameters": {
"fromIndex": 0,
"toIndex": 2,
"weight": 1000.0,
"id": "ABC-001",
"type": "PARCEL"
},
"properties": {
"allocation": 0.13,
"distance": 76764
}
},
{
"co2e": {
"total": 106245,
"wtt": 24443,
"ttw": 81802,
"intensity": 0.27681
},
"parameters": {
"fromIndex": 0,
"toIndex": 2,
"weight": 5000.0,
"id": "ABC-002",
"type": "PARCEL"
},
"properties": {
"allocation": 0.67,
"distance": 76764
}
},
{
"co2e": {
"total": 16902,
"wtt": 3888,
"ttw": 13013,
"intensity": 0.13334
},
"parameters": {
"fromIndex": 0,
"toIndex": 3,
"weight": 1000.0,
"id": "ABC-003",
"type": "PARCEL"
},
"properties": {
"allocation": 0.11,
"distance": 126762
}
},
{
"co2e": {
"total": 13278,
"wtt": 3055,
"ttw": 10223,
"intensity": 0.14553
},
"parameters": {
"fromIndex": 1,
"toIndex": 3,
"weight": 1000.0,
"id": "ABC-004",
"type": "PARCEL"
},
"properties": {
"allocation": 0.08,
"distance": 91235
}
}
],
"parameters": {
"locations": {
"0": "FRFOS",
"1": "FRMTU",
"2": "FRMRS",
"3": "FRSDP",
"4": "FRFOS"
},
"details": {
"identifier": null,
"truckSize": 26,
"fuel": "DIESEL",
"carrier": null
}
},
"properties": {
"allocationMethod": "ISO_14083_TRANSPORT_ACTIVITY_ALLOCATION",
"dataType": "MODELED",
"distance": 175508,
"emptyRunning": 0.28,
"legs": [
{
"from": {
"locode": "FRFOS",
"coordinates": [
4.848286968176052,
43.407186268695995
],
"city": "Fos-sur-Mer",
"country": "France"
},
"to": {
"locode": "FRMTU",
"coordinates": [
5.05526,
43.40735
],
"city": "Martigues",
"country": "France"
},
"distance": 35527,
"region": "Europe",
"loadFactor": 0.35
},
{
"from": {
"locode": "FRMTU",
"coordinates": [
5.05526,
43.40735
],
"city": "Martigues",
"country": "France"
},
"to": {
"locode": "FRMRS",
"coordinates": [
5.34005236625671,
43.3317317962646
],
"city": "Marseille",
"country": "France"
},
"distance": 41237,
"region": "Europe",
"loadFactor": 0.4
},
{
"from": {
"locode": "FRMRS",
"coordinates": [
5.34005236625671,
43.3317317962646
],
"city": "Marseille",
"country": "France"
},
"to": {
"locode": "FRSDP",
"coordinates": [
5.1,
43.63333333333333
],
"city": "Salon-de-Provence",
"country": "France"
},
"distance": 49998,
"region": "Europe",
"loadFactor": 0.1
},
{
"from": {
"locode": "FRSDP",
"coordinates": [
5.1,
43.63333333333333
],
"city": "Salon-de-Provence",
"country": "France"
},
"to": {
"locode": "FRFOS",
"coordinates": [
4.848286968176052,
43.407186268695995
],
"city": "Fos-sur-Mer",
"country": "France"
},
"distance": 48746,
"region": "Europe",
"loadFactor": 0.0
}
],
"truck": {
"minSize": 26.0,
"maxSize": 32.0,
"identifier": "EURO6",
"fuel": "DIESEL"
}
}
}In this example, the final leg (FRSDP β FRFOS) has loadFactor = 0, meaning it is an empty repositioning movement contributing to total emissions but not directly to shipment allocation.
π’ Vessel APIs
Improved error handling on /vessel/v2/timeseries
/vessel/v2/timeseriesRequests exceeding the maximum number of returnable positions now return a clear 400 error with guidance on how to adjust your time range. We've also updated the documentation with recommended range limits based on vessel activity patterns.
You made it to the end of our changelog, thanks for reading ππΌWe'd love to hear your thoughts on these updates. Send us your feedback at [email protected]!