# Sending planning data as OTM5 file

{% admonition type="info" %}
We have updated this page, for the outdated but still supported information [see the older page](planning-otm5_old.mdx)
{% /admonition %}

One of the formats we are able to consume is JSON and specifically in the [OTM5
standard](https://otm5.opentripmodel.org/). 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](../otm5/otm5-concepts.md#uuids) for more information on how to deal with
UUIDs.

<!-- Also update the export and generic OTM pages if this changes -->
## A look at the basic format
The OTM5 planning intake follows the rules of the [Trip
Profile](../otm5/otm5-trip-profile.md) also used in the data export. 

The OpenAPI specification can be found
[here](https://planning-receive-api.services.simacan.com/api/v1#tag/OTM5/paths/~1otm~1api~1v5~1trips/put).

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:
```md
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](../otm5/otm5-concepts.md#uuids)) 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.
  
  You can choose to provide **either** the UUID **or** the `tripId`, but it is **not recommended** to send both.  
  If both are included, all parties must maintain a **1-to-1 relationship** between them consistently over time — 
  including after trip cancellation.
  ### Example
  
  #### 1. UUID not provided
  If only the `tripId` is provided, the system generates a UUID deterministically:
  ```json
  {
    "externalAttributes": {
      "tripId": "12345-ABC"
    }
  }
  ```
  
  #### 2. UUID provided without `tripId`
    Only the UUID is used to identify the trip.
    ```json
    {
      "uuid": "a1b2c3d4-e5f6-7890-ab12-34567890cdef"
    }
    ```
    
  #### 3. both UUID and `tripId` provided
    ⚠️ In this case, all parties must guarantee that "12345-ABC" will always map to "a1b2c3d4-e5f6-7890-ab12-34567890cdef", 
    even if the trip is canceled or re-created. If this mapping is broken later, systems may no longer correctly reference 
    the same trip
  ```json
       {
         "uuid": "a1b2c3d4-e5f6-7890-ab12-34567890cdef",
         "externalAttributes": {
           "tripId": "12345-ABC"
         }
       }
  ```
       
- 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 when `cancelling` 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):
```json
{
  "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.

```json
  "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`.

```json
  "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` and
  `vehicleType`.
- 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:
  1. First we check the UUID of the sensor and check if that entity exists within the platform.
  2. If not, we check the combination of sensor and system IDs within the sensor.
  3. Then we check for the vehicle ID. Note that we recommend against using the
     `vehicleId` to identify a device. The `vehicleId` should identify the
     physical vehicle and is only relevant if the tracking device is part of the
     vehicle.
  4. Lastly we check for the license plate.

```json
    "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 an `endTime`.
- A stop can have multiple `load` and `unload` actions; these actions have a
  `consignment`.
- A stop optionally has a display `name`, `remark` and `sequenceNr`.
- A stop optionally has a `externalId` within an `externalAttribute`, 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:
  ```json
    {
      "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:
```json
{
    "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 the `externalAttributes`
  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` and `country`.
* 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 types `operationalBase` and `warehouse` are used for managed
  locations and appear in Master Data.
* The location has optional contact details. This option can be used in case there is a contact at the location but 
  it is neither the consignee nor the consignor. For example, to reach an administration desk or a security warden.
  Currently, we support the fields `phone, email, name, lastName, firstName`.

A _referenced_ location could look like this (it needs to be present on the
Simacan platform before though):
```json
  "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).

```json
  "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:  
```json
{
  "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:
```json
     "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 as `consignmentId`,
  `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.

```json
{
  "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':

```json
{
  "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:
```json
    "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):
```json
   "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:
```json
   {
    "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.

```json
{
  "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"
  }]
}
```
