Sending planning data as OTM5 file
info
We have updated this page, for the outdated but still supported information see the older page
One of the formats we are able to consume is JSON and specifically in the OTM5 standard. Below you will find more information on how to arrange your OTM5 file. Let's get started.
Updating your planning
It is possible to update a planning that you sent to us before. It is important that sent messages always contains a full state, so a full planning. Any IDs we receive, will always be matched to any previous data, wherever possible. A trip id (UUID) used, that remained the same matched to a previous trip ID, will update the known trip. A new trip id (UUID) will secondly be checked on externalAttributes>tripId. If the UUID and the tripId don't match with a previous id, the new trip ID will be considered as a new trip. Stop IDs that remain the same, will be considered as the previous sent stop or changes to the previous sent stop. New stop IDs are considered as new stops. Removed stop IDs will be marked as cancelled stops. See also the UUID page for more information on how to deal with UUIDs.
A look at the basic format
The OTM5 planning intake follows the rules of the Trip Profile also used in the data export.
The OpenAPI specification can be found here.
To give a little more insight, here is an overview of how sending trips in the OTM5 format works.
The basic structure looks like this:
trip
uuid (unique ID that is used for identifying the trip & future for updates)
name (the display name)
externalAttributes
status (only there when cancelled, actions are then empty/ignored)
actors (shipper, carrier, subcontractor, driver)
vehicle (optional)
actions
stop
location
start and end times
actions
attachTransportEquipment (trailer assignment)
wait/break
load/unload
consignment
goods
move
from (location reference)
to (location reference)
actions
wait/break
move (first/last mile)
stop
...
Trip
As mentioned before, the trip is the top-level entity and ties together all information that is relevant for all visiting multiple locations and loading and unloading consignments on those locations.
A trip has the following properties:
- The trip has a unique UUID (see also the UUID
page) that is used to identify this
particular trip. This UUID is thus also used whenever the trip needs updates.
Since not all planning systems work with UUIDs, it is optional. When not
present the
tripId
in the external attributes will be used to deterministically generate a UUID. - A trip has an optional
name
(display name), which is to easily recognize it in the Control Tower. Note though that is doesn't have to be unique but usually is for a single day to differentiate trips on that day. - A trip optionally has
externalAttributes
, these are also known as customer data and do not serve a role on the Simacan platform, but can be used for grouping trips and shared with other parties where they might be essential. When receiving trips from the Simacan platform these external attributes also contain enriched data from the platform. - A trip at some point needs to be coupled to a vehicle for effective monitoring, but this does not need to happen when the planning is inserted.
- A trip is required to have
actions
: at least two stops are necessary. The one notably exception is whencancelling
a trip. No actions are shared for cancelled trips. - A trip is required to have
actors
, usually a shipper and a carrier, but also a driver and subcontractor are possible.
Example
Here is a basic example (that will be receive more details for each sub-entity):
{
"id": "2a8af6c6-e4d5-5dfb-9532-e8b17c3ffd88",
"name": "2021-32-2-ABC",
"externalAttributes": {
"shift": "morning shift",
"tripId": "external-system-id"
},
"vehicle": {},
"actors": [],
"actions": []
}
External Attributes
A trip optionally has a shift
and/or externalId
within the
externalAttributes
object. More attributes can be provided and will be saved
in the Control Tower as customer data. This data serves no purpose except for
filtering trips that have the same values for the same keys.
"externalAttributes": {
"tripId": "some-unique-id",
"shift": "Evening shift",
"some-custom-field": "some-custom-value"
}
Actors
Actors are organisations or individuals that have a role in the logistic operation. Within Simacan they can appear on the trip (shipper, carrier, subcontractor, driver), or on the consignment (consignee, consignor). The shipper and carrier are always expected on the trip level. The driver is optional, as are the consignor and consignee on the consignment.
Actors have the following properties:
- An actor has a unique Simacan identifier (
code
). - An actor has one or multiple
roles
.
"actors": [
{
"entity": {
"externalAttributes": {
"externalId": "unique_id_carrier"
}
},
"roles": [ "carrier" ],
"associationType": "inline"
},
{
"entity": {
"externalAttributes": {
"externalId": "unique_id_shipper"
}
},
"roles": [ "shipper" ],
"associationType": "inline"
},
...
]
Vehicle/Sensor data
Ultimately a trip needs to be coupled to a vehicle to be able to be executed.
However, for effective monitoring Simacan actually doesn't need the vehicle,
since it also suffices to provide the device that is placed in the vehicle and
emits the location updates. This device can be part of a vehicle (or the
tracking unit attached to the vehicle), but it also could be the phone of the
driver. Within OTM both these situations are modeled using the vehicle
,
possibly in the combination with a sensor
.
Note that OTM distinguishes towing and towed 'vehicles' by modeling the
latter as transportEquipment
and not vehicles, so the vehicle
field only
contains information about the towing vehicle.
A vehicle contains the following properties:
- A vehicle is a physical machine with cargo space able to transport goods.
- Possibly a sensor is placed within that vehicle (OTM5 supports multiple sensors in a vehicle, but within Simacan we support only one).
- The vehicle can contain properties such as
fuel
(type),licensePlate
andvehicleType
. - The vehicle might contain a unique identifier in the external attributes.
- The vehicle does not contain any information about trailers. This information is added in the actions of the trip.
- Within Simacan the vehicle/sensor already needs to be known on our platform to couple it to a trip. It is not possible to create vehicles and/or sensors from within a planning.
- Coupling happens in the following order:
- First we check the UUID of the sensor and check if that entity exists within the platform.
- If not, we check the combination of sensor and system IDs within the sensor.
- Then we check for the vehicle ID. Note that we recommend against using the
vehicleId
to identify a device. ThevehicleId
should identify the physical vehicle and is only relevant if the tracking device is part of the vehicle. - Lastly we check for the license plate.
"vehicle": {
"entity": {
"id": "3de444dd-81cf-4e69-b27b-82e92e0dbda3",
"externalAttributes": {
"vehicleId": "my-external-id"
},
"vehicleType": "truck",
"fuel": "Euro95",
"licensePlate": "AA-12-BB",
"sensors": [{
"associationType": "inline",
"entity": {
"id": "6827dac5-776e-4474-a927-0cf695b4d9d3",
"externalAttributes": {
"sensorId": "id-of-the-device-in-the-vehicle",
"systemId": "system-of-the-fms-device"
}
}
}]
},
"associationType": "inline"
}
Actions
The most interesting information of the trip is present in the actions. These contain the locations that need to be visited, when, and what happens on those locations. Actions consists of stops and moves. Stops contain the information of visiting a location between certain time intervals and doing other actions on that locations such as loading and unloading consignments. Moves contain the information of moving between the locations of a stop. On these moves other actions can be done as well, such as having a break, or following a specific route (first/last miles). Naturally, the actions of a trip thus always start and end with a stop, with moves in between. However, depending on the use case, moves are left out since they do not contain any relevant information.
Stop
A stop indicates visiting a location within a time interval and doing actions on that location. It consists of the following properties:
- A stop has a
location
,startTime
and anendTime
. - A stop can have multiple
load
andunload
actions; these actions have aconsignment
. - A stop optionally has a display
name
,remark
andsequenceNr
. - A stop optionally has a
externalId
within anexternalAttribute
, this identifier can be used for updating this stop. If none is present the UUID of the stop will be used. - To cancel a stop, simply do not send it in subsequent messages.
- A stop can be marked administrative. This means the stop is only their for
administrative purposes and is not actively monitored in the Control Tower.
These stops do not have ETAs and are not actively visited. This information is
provided in the
externalAttributes
of the stop by having a key-value pair"monitoring": "administrative"
. Non-administrative stops will simply not have such an attribute. - A stop optionally has time constraints, wich in code looks like:
{ "id": "48c484f7-54ad-4209-b251-44abef26f6b0", "value": { "startTime": "2021-03-12T10:00:00Z", "endTime": "2021-03-12T11:00:00Z", "type": "timeWindowsConstraint" } }
A stop (with omitted location and consignment) could look like:
{
"entity": {
"id": "cd5be9b4-eb2e-50c1-bac9-d78d159c9d8e",
"lifecycle": "planned",
"externalAttributes": {
"stopId": "stop123"
},
"location": {},
"startTime": "2021-02-24T08:00:00Z",
"endTime": "2021-02-24T08:20:00Z",
"actions": [{
"entity": {
"id": "85a48825-0d59-328b-b571-e3283a1f7fe7",
"lifecycle": "planned",
"consignment": {},
"startTime": "2021-02-24T08:00:00Z",
"endTime": "2021-02-24T08:15:00Z",
"actionType": "unload"
},
"associationType": "inline"
},
{
"entity": {
"id": "b7a632a2-4f2b-4efb-b75c-17132436cf39",
"lifecycle": "planned",
"consignment": {},
"startTime": "2021-02-24T08:15:00Z",
"endTime": "2021-02-24T08:20:00Z",
"actionType": "load"
},
"associationType": "inline"
}
],
"sequenceNumber": 0,
"constraint": {
"entity": {
"id": "10877d0c-ca94-459b-84aa-0cf10de35af6",
"value": {
"startTime": "2021-02-24T07:30:00Z",
"endTime": "2021-02-24T09:30:00Z",
"type": "timeWindowConstraint"
},
"associationType": "inline"
},
"actionType": "stop"
}
}
}
Location
Locations are the actual physical places visited during stops where actions - such as loading and unloading consignments - are being executed.
Within Simacan we deal with two types of locations:
- managed locations, those that are reused for different trips repeatedly. Managed locations also are visible in Master Data, can have manual geofences, and first/last mile guidance.
- unmanaged locations, those are only of interst in one planning and thus are not visible in Master Data. These locations do not have manual geofences, and no first or last mile guidance.
Both managed and unmanaged locations can be shared with and from Simacan using inline locations.
Inline locations have the following properties:
- The location contains a
name
used for display purposes. - The location optionally contains a
locationId
in theexternalAttributes
that is used for identification in Master Data and repeated use after inlining. - The location optionally contains a coordinates.
- The location contains address information such as the
street
,houseNumber
,postalCode
,city
andcountry
. - The location contains a type. The
customer
is used for one-off (unmanaged) locations and are treated as sensitive data. They also do not appear in Master Data. The typesoperationalBase
andwarehouse
are used for managed locations and appear in Master Data. - The location has optional contact details.
A referenced location could look like this (it needs to be present on the Simacan platform before though):
"location": {
"associationType": "attributeRestriction",
"restriction": {
"externalAttributes": {
"locationId": "someId"
}
},
"entityType": "location"
}
Whenever a location is unknown (unmanaged) or needs to be updated (both managed
and unmanaged) you can provide a location 'inline'. Note that the type
of a
location is either customer
(unmanaged), warehouse
or operationalBase
(both managed).
"location": {
"entity": {
"id": "a1463169-8e75-4c0c-8d4f-fbeb408f53aa",
"geoReference": {
"name": "Simacan",
"street": "Valutaboulevard",
"houseNumber": "16",
"postalCode": "1234AB",
"city": "Amersfoort",
"type": "addressGeoReference"
},
"type": "customer"
},
"associationType": "inline"
}
An inline location optionally has lat/lon-coordinates & contact details:
{
"id": "440fffc6-3d2f-4cb2-9b98-93f0f1a87f3a",
"geoReference": {
"lat": 52.370216,
"lon": 4.895168,
"type": "latLonPointGeoReference"
},
"administrativeReference": {
"name": "Simacan",
"street": "Valutaboulevard",
"houseNumber": "16",
"postalCode": "1234AB",
"city": "Amersfoort"
},
"contactDetails": [{
"value": "Simacan",
"type": "name"
},
{
"value": "0612345678",
"type": "phone"
},
{
"value": "sim@can.com",
"type": "email"
}
],
"type": "warehouse"
}
Lastly, it is possible to provide an 'address-less' location, that only consists of a name and coordinates. These locations are always unmanged:
"location": {
"entity": {
"name": "Location on some road",
"geoReference": {
"lat": 51.9445,
"lon": 4.369,
"type": "latLonPointGeoReference"
},
"type": "customer"
},
"associationType": "inline"
},
Consignment
A consignment is the administrative label put on a set of goods that are shipped
together. A consigment does not physically exist and serves only to group goods
together, provide some additional information like consignor and consignee and
have somethign that be tracked during the lifetime of shpping the goods. The
consignment is present in the trip by being the subject of the load
, unload
and handOver
actions.
Within the Simacan platform the consignment has the following properties:
- A consignment has a
type
(free text), this is what is known in the SCT as the goodsFlowType. - A consignment optionally has
externalAttributes
such asconsignmentId
,orderId
,userId
,trackTraceCode
. - A consignment optionally has
goods
. Though goods are the meat of what is being transported, Simacan specializes in monitoring and thus is less concerned with what is actually shipped. A good has a quantity and optionally a productType, barCode and remark. Goods items and transportEquipment are both supported for the intake but will ultimately be translated to transportEquipment. - Consignments can contain actors for the consignor and consignee. This can be the same actor.
{
"id": "a556da93-46f3-4df5-8269-263bb337fde4",
"actors": [
{
"associationType": "inline",
"entity": { "name": "some consignor" },
"roles": [ "consignor" ]
},
{
"associationType": "inline",
"entity": { "name": "some consignee" },
"roles": [ "consignee" ]
}
],
"externalAttributes": {
"orderId": "<order id>",
"userId": "<user id>",
"trackTraceCode": "<t&t code>"
},
"type": "Frozen goods",
"goods": [
{
"entity": {
"id": "b74d1b1a-905a-42dd-91be-1fe77f443467",
"remark": "Be careful not to melt it!",
"barCode": "123456789",
"quantity": 3,
"productType": "Ice cream",
"type": "items"
},
"associationType": "inline"
}
]
}
Special situations
Cancelations
It is also possible to cancel a trip with OTM5. This message contains the trip ID, the actors for that trip, and the status 'cancelled':
{
"id": "b4c9036f-8d97-4aab-ac25-34da94976849",
"status": "cancelled",
"actors": [
{
"entity": {
"externalAttributes": {
"externalId": "unique_id_carrier"
}
},
"roles": [ "carrier" ],
"associationType": "inline"
},
{
"entity": {
"externalAttributes": {
"externalId": "unique_id_shipper"
}
},
"roles": [ "shipper" ],
"associationType": "inline"
},
]
}
Trip vehicle data
Regardless if you provide a regular trip or a groupage trip, in both cases it is also possible but not mandatory to provide vehicle data. The vehicle data should at least contain either a licensePlate or an external ID (or both). On top of that it is possible, but not required to provide a vehicle type and fuel type:
"vehicle": {
"entity": {
"id": "3de444dd-81cf-4e69-b27b-82e92e0dbda3",
"externalAttributes": {
"vehicleId": "my-external-id"
},
"vehicleType": "truck",
"fuel": "Euro95",
"licensePlate": "AA-12-BB"
},
"associationType": "inline"
}
If you provide a vehicle it is also possible to provide driver and/or trailer. You can add a driver by adding an actor to the trip (on top of the shipper and carrier):
"actors": [
{
"associationType": "attributeRestriction",
"restriction": {
"externalAttributes": {
"carrierId": "<my-carrier>"
}
},
"entityType": "actor",
"roles": ["carrier"]
},
{
"associationType": "attributeRestriction",
"restriction": {
"externalAttributes": {
"shipperId": "<my-shipper>"
}
},
"entityType": "actor",
"roles": ["shipper"]
},
{
"associationType": "attributeRestriction",
"restriction": {
"externalAttributes": {
"driverId": "<name of the driver>"
}
},
"entityType": "actor",
"roles": ["driver"]
}
]
Lastly, it is possible to attach a trailer by adding an
attachTransportEquipment
action on the first stop, before or after loading and
unloading:
{
"entity": {
"id": "028ad890-38fb-44d8-a0d3-304f0dbe8e31",
"transportEquipment": {
"entity": {
"id": "2fbe0dfe-c15a-4cee-a807-f462318d6f35",
"licensePlate": "00-AAA-0"
},
"associationType": "inline"
},
"startTime": "2021-06-28T04:00:00Z",
"endTime": "2021-06-28T04:01:00Z",
"actionType": "attachTransportEquipment"
},
"associationType": "inline"
}
Full message example
Combining the ruleset above and taking into account required and optional fields, we can compose the following message. This is an example message, with three stops, all filled with dummy values. You could copy and paste and replace this with your own data.
{
"id": "8aef6638-3828-45b6-9391-3e2882512345",
"name": "OTM5 Trip Example",
"externalAttributes": {
"tripId": "25/04/21|OTM5-EXAMPLE-TRIP"
},
"actors": [
{
"entity": {
"externalAttributes": {
"externalId": "unique_id_carrier"
}
},
"roles": [ "carrier" ],
"associationType": "inline"
},
{
"entity": {
"externalAttributes": {
"externalId": "unique_id_shipper"
}
},
"roles": [ "shipper" ],
"associationType": "inline"
}
],
"actions": [{
"entity": {
"name": "S",
"externalAttributes": {
"stopId": "25/04/21|OTM5-EXAMPLE-TRIP-start"
},
"lifecycle": "planned",
"location": {
"associationType": "attributeRestriction",
"restriction": {
"externalAttributes": {
"locationId": "HUB Amsterdam"
}
},
"entityType": "location"
},
"startTime": "2021-04-25T17:36:00Z",
"endTime": "2021-04-25T17:51:00Z",
"actions": [{
"entity": {
"id": "e86bd334-f62c-45fe-8a8b-661cde187654",
"consignment": {
"entity": {
"id": "6d0931ef-f9a1-4267-be7c-758aef154321",
"type": "Products to load",
"goods": [{
"entity": {
"id": "3ddcac3b-c349-4e08-bab5-f39925145678",
"barCode": "x-0006",
"quantity": 12,
"productType": "Newspaper",
"type": "items"
},
"associationType": "inline"
}, {
"entity": {
"id": "86511ae4-8956-4df3-b3c5-0c0efc355555",
"barCode": "x-0012",
"quantity": 1,
"productType": "Book",
"type": "items"
},
"associationType": "inline"
}, {
"entity": {
"id": "7a6f6039-2f01-40a7-a557-6f2dbf022222",
"barCode": "x-0013",
"quantity": 1,
"productType": "Champagne",
"type": "items"
},
"associationType": "inline"
}]
},
"associationType": "inline"
},
"startTime": "2021-04-25T17:36:00Z",
"endTime": "2021-04-25T17:51:00Z",
"actionType": "load"
},
"associationType": "inline"
}],
"constraint": {
"entity": {
"value": {
"startTime": "2021-04-25T17:35:00Z",
"endTime": "2021-04-25T18:35:00Z",
"type": "timeWindowsConstraint"
}
},
"associationType": "inline"
},
"actionType": "stop"
},
"associationType": "inline"
}, {
"entity": {
"name": "1",
"externalAttributes": {
"stopId": "25/04/21|OTM5-EXAMPLE-TRIP-SOME-STOP"
},
"lifecycle": "planned",
"remark": "Doorbell is broken, please knock on door, place on doormat when not home",
"location": {
"entity": {
"id": "dff6de51-a24c-4f8b-8a12-ea8205577777",
"name": "Shirley",
"geoReference": {
"lat": 52.32686,
"lon": 4.88846,
"type": "latLonPointGeoReference"
},
"type": "customer",
"administrativeReference": {
"name": "Shirley",
"street": "Nieuw Herlaer 2",
"postalCode": "1083 BD",
"city": "Amsterdam",
"country": "NL"
},
"contactDetails": [{
"value": "Shirley",
"type": "firstName"
}, {
"value": "Shirley de Boer",
"type": "name"
}, {
"value": "+31612345678",
"type": "phone"
}, {
"value": "shirleydeboer@shotmail.com",
"type": "email"
}]
},
"associationType": "inline"
},
"startTime": "2021-04-25T18:01:00Z",
"endTime": "2021-04-25T18:03:00Z",
"actions": [{
"entity": {
"lifecycle": "planned",
"consignment": {
"entity": {
"id": "a359a0f6-d695-45d6-9fa8-afcfb7233333",
"externalAttributes": {
"orderId": "6028716",
"userId": "599271",
"trackTraceCode": "602CLV05",
"orderType": "Subscriptions"
},
"type": "OVERIG",
"goods": [{
"entity": {
"id": "86511ae4-8956-4df3-b3c5-0c0efc355555",
"barCode": "x-0012",
"quantity": 1,
"productType": "Book",
"type": "items"
},
"associationType": "inline"
}, {
"entity": {
"id": "3ddcac3b-c349-4e08-bab5-f39925145678",
"barCode": "x-0006",
"quantity": 1,
"productType": "Newspaper",
"type": "items"
},
"associationType": "inline"
}]
},
"associationType": "inline"
},
"startTime": "2021-04-25T18:01:00Z",
"endTime": "2021-04-25T18:03:00Z",
"actionType": "unload"
},
"associationType": "inline"
}],
"constraint": {
"entity": {
"value": {
"startTime": "2021-04-25T18:00:00Z",
"endTime": "2021-04-25T19:00:00Z",
"type": "timeWindowsConstraint"
}
},
"associationType": "inline"
},
"actionType": "stop"
},
"associationType": "inline"
}, {
"entity": {
"name": "2",
"externalAttributes": {
"stopId": "25/04/21|OTM5-EXAMPLE-TRIP-SOME-STOP-2"
},
"lifecycle": "planned",
"remark": "Leave at neighbours when not at home.",
"location": {
"entity": {
"id": "ece51c99-0a6c-458d-ab44-91c77549bbbb",
"name": "Martin",
"geoReference": {
"lat": 52.31945,
"lon": 4.88408,
"type": "latLonPointGeoReference"
},
"type": "customer",
"administrativeReference": {
"name": "Martin",
"street": "Aanloop 3",
"postalCode": "1183SZ",
"city": "Amstelveen",
"country": "NL"
},
"contactDetails": [{
"value": "Martin",
"type": "firstName"
}, {
"value": "Martin Golf",
"type": "name"
}, {
"value": "+31687654321",
"type": "phone"
}, {
"value": "martingolf@igoogle.nl",
"type": "email"
}]
},
"associationType": "inline"
},
"startTime": "2021-04-25T18:04:00Z",
"endTime": "2021-04-25T18:06:00Z",
"actions": [{
"entity": {
"lifecycle": "planned",
"consignment": {
"entity": {
"id": "e5f0528c-32b8-4a57-af15-57c297db4444",
"externalAttributes": {
"orderId": "6021923",
"userId": "199283",
"trackTraceCode": "60ABC04",
"orderType": "Subscriptions"
},
"type": "OVERIG",
"goods": [{
"entity": {
"id": "7a6f6039-2f01-40a7-a557-6f2dbf022222",
"barCode": "x-0013",
"quantity": 1,
"productType": "Champagne",
"type": "items"
},
"associationType": "inline"
}]
},
"associationType": "inline"
},
"startTime": "2021-04-25T18:04:00Z",
"endTime": "2021-04-25T18:06:00Z",
"actionType": "unload"
},
"associationType": "inline"
}],
"constraint": {
"entity": {
"value": {
"startTime": "2021-04-25T18:00:00Z",
"endTime": "2021-04-25T19:00:00Z",
"type": "timeWindowConstraint"
}
},
"associationType": "inline"
},
"actionType": "stop"
},
"associationType": "inline"
}]
}