Transportation Plans

The transportation plan API is provided to model the movement of goods across multiple modes of transportation. An example would be a container that is offloaded from a ship and delivered to a facility as a drayage mode, then unpacked and loaded into a dry van as a transload, and finally transported to the end customer as a full truck load. The underlying data model contains the following primary data objects:

  • Transportation Plan - the top level object with metadata and is the parent of stages
  • Service - defines a service provider like a transload, storage, or terminal
  • Shipment - the v2/shipment model
  • Stage - container for services and legacy shipments

The advantage of using the transportation plan model is it allows aggregation across all of the stages to do things like calculate the total cost of moving goods across multiple modes and service providers, assign different carriers to different stages of the movement of goods, and allocate different financial accounting to the different stages of the movement.

Creating transportation plans via the UI

Let’s explore how to implement a movement of goods that includes drayage → transload → full truck load to see how the transportation plan API is used to model it. To begin, here is how the UI is used to create a transportation plan, seen on the action panel below.

Next, we want to create a transload stage following the drayage move. Which if performed from the UI would be done by adding a new stage the drayage stage.

A modal is presented when adding a new stage and in our case we want to create a transload stage.

Details are provided for the transload service provider and the stage is created.

Although the UI implies the drayage stage has been created, in actuality it is created just prior to creating the transload stage. First, a multi-stage shipment is created with a drayage stage, and then the transload stage is created. Resulting in both stages being displayed in the action panel UI for the drayage shipment.

Adding an additional full truckload stage is performed the same way as adding a transload stage except FTL is selected in the model instead of Transload.

Using the API to create a multi-stage shipment

To perform the same actions using the API start by creating a drayage mode v2/shipment using the existing APIs to POST a v2/shipment. Documentation can be found here.

Create a Transportation Plan shell

Note!

The current routes will be updated shortly and usage of the current transportation plan routes will be supported going forward. The changes are in naming only, not functionality. The intention is to avoid ambiguity between the current shipment model and the new transportation plan model. The name changes will include the following:

  • All /shipment routes will be renamed to /transportation-plan
  • legacy_shipment_id will be renamed shipment_id
  • legacy_shipment will be renamed shipment

The examples below use the current naming convention for the routes and response bodies that will be updated in the future.

Create a transportation plan using the POST /shipments route to act as the container for the stages we will be creating. There are no required parameters in the request body and in this example none were provided. The resulting response body contains id of the newly created transportation plan.

Example request
curljavascriptpythonjava
Copy
Copied
curl --location --request POST 'https://sandbox-api.shipwell.com/shipments' \
--header 'Authorization: YOUR_AUTHORIZATION_HEADER'
Copy
Copied
const basePath = "";
const hostPath = "sandbox-api.shipwell.com";
const resp = await fetch(
  `https://${hostPath}${basePath}/shipments`,
  {
    method: "POST",
    headers: {
      "Authorization": "YOUR_AUTHORIZATION_HEADER"
    }
  }
);

const data = await resp.json();
console.log(data);
Copy
Copied
import requests

base_path = ""
host_path = "sandbox-api.shipwell.com"
target_url = (
    "https://"
    + host_path
    + base_path
    + "/shipments"
)

headers = {"Authorization": "YOUR_AUTHORIZATION_HEADER"}

response = requests.post(target_url, headers=headers)
data = response.json()
print(data)
Copy
Copied
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

OkHttpClient client = new OkHttpClient();

String basePath = "";
String hostPath = "sandbox-api.shipwell.com";
String targetUrl = "https://" +
  host_path +
  base_path +
  "/shipments";

Request request = new Request.Builder()
  .url(targetUrl)
  .header("Authorization", "YOUR_AUTHORIZATION_HEADER")
  .build();

try (Response response = client.newCall(request).execute()) {
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  response.body().string();
}
Example response
Copy
Copied
{
    "created_at": "2022-12-02T18:39:07.256021+00:00",
    "updated_at": "2022-12-02T18:39:07.256021+00:00",
    "name": null,
    "archived": false,
    "instructions": null,
    "references": [],
    "mode_specific_data": [],
    "id": "01GKA255FRSJ7Y1251HE5Q78DR",
    "reference_id": "QQBRR0",
    "created_by": {
        "tenant_id": "a54ef012-77d4-44a0-8ba5-115b09b655be",
        "user_id": "f0b979c9-28e2-41d6-883e-cb1f5c320bb4"
    },
    "overall_status": "DRAFT",
    "stages": [],
    "contacts": [],
    "items": [],
    "stops": [],
    "customer": null
}

Add the drayage shipment as the first stage

Add the drayage shipment created previously as the first stage of the transportation plan by using the POST /shipments/{shipment_id}/legacy-shipment-stages route providing the newly created transportation plan id as the path parameter and the drayage shipment id in the body of the request as the legacy_shipment_id. The response body contains details of the newly created stage.

Example request
curljavascriptpythonjava
Copy
Copied
curl --location --request POST 'https://sandbox-api.shipwell.com/shipments/{shipment_id}/legacy-shipment-stages' \
--header 'Authorization: YOUR_AUTHORIZATION_HEADER'
Copy
Copied
const shipmentId = "YOUR_shipmentId_PARAMETER"
const basePath = "";
const hostPath = "sandbox-api.shipwell.com";
const resp = await fetch(
  `https://${hostPath}${basePath}/shipments/${shipmentId}/legacy-shipment-stages`,
  {
    method: "POST",
    headers: {
      "Authorization": "YOUR_AUTHORIZATION_HEADER"
    }
  }
);

const data = await resp.json();
console.log(data);
Copy
Copied
import requests

shipment_id = "YOUR_shipmentId_PARAMETER"
base_path = ""
host_path = "sandbox-api.shipwell.com"
target_url = (
    "https://"
    + host_path
    + base_path
    + "/shipments/"
    + shipment_id
    + "/legacy-shipment-stages"
)

headers = {"Authorization": "YOUR_AUTHORIZATION_HEADER"}

response = requests.post(target_url, headers=headers)
data = response.json()
print(data)
Copy
Copied
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

OkHttpClient client = new OkHttpClient();

String shipmentId = "YOUR_shipmentId_PARAMETER";
String basePath = "";
String hostPath = "sandbox-api.shipwell.com";
String targetUrl = "https://" +
  host_path +
  base_path +
  "/shipments/" + 
  shipmentId + 
  "/legacy-shipment-stages";

Request request = new Request.Builder()
  .url(targetUrl)
  .header("Authorization", "YOUR_AUTHORIZATION_HEADER")
  .build();

try (Response response = client.newCall(request).execute()) {
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  response.body().string();
}
Example response
Copy
Copied
{
    "id": "01GKA2E6TFS7KPYJZPK6T4B5GY",
    "shipment_id": "01GKA255FRSJ7Y1251HE5Q78DR",
    "stage_type": "LEGACY_SHIPMENT",
    "directly_dependent_stages": [],
    "directly_depends_on_stages": [],
    "legacy_shipment_id": "b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e",
    "legacy_shipment": {
        "id": "b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e",
        "resource_type": "shipment",
        "self_link": "https://sandbox-api.shipwell.com/v2/shipments/b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e/"
    }
}

Use the GET /shipments/{shipment_id)/stages route with the transportation plan id as the path parameter to review the stages of the transportation plan and note that the response contains the drayage legacy shipment added.

Example request
curljavascriptpythonjava
Copy
Copied
curl --location --request GET 'https://sandbox-api.shipwell.com/shipments/{shipment_id}/stages' \
--header 'Authorization: YOUR_AUTHORIZATION_HEADER'
Copy
Copied
const shipmentId = "YOUR_shipmentId_PARAMETER"
const basePath = "";
const hostPath = "sandbox-api.shipwell.com";
const resp = await fetch(
  `https://${hostPath}${basePath}/shipments/${shipmentId}/stages`,
  {
    method: "GET",
    headers: {
      "Authorization": "YOUR_AUTHORIZATION_HEADER"
    }
  }
);

const data = await resp.json();
console.log(data);
Copy
Copied
import requests

shipment_id = "YOUR_shipmentId_PARAMETER"
base_path = ""
host_path = "sandbox-api.shipwell.com"
target_url = (
    "https://"
    + host_path
    + base_path
    + "/shipments/"
    + shipment_id
    + "/stages"
)

headers = {"Authorization": "YOUR_AUTHORIZATION_HEADER"}

response = requests.get(target_url, headers=headers)
data = response.json()
print(data)
Copy
Copied
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

OkHttpClient client = new OkHttpClient();

String shipmentId = "YOUR_shipmentId_PARAMETER";
String basePath = "";
String hostPath = "sandbox-api.shipwell.com";
String targetUrl = "https://" +
  host_path +
  base_path +
  "/shipments/" + 
  shipmentId + 
  "/stages";

Request request = new Request.Builder()
  .url(targetUrl)
  .header("Authorization", "YOUR_AUTHORIZATION_HEADER")
  .build();

try (Response response = client.newCall(request).execute()) {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
    response.body().string();
}
Example response
Copy
Copied
{
    "data": [
        {
            "id": "01GKA2E6TFS7KPYJZPK6T4B5GY",
            "shipment_id": "01GKA255FRSJ7Y1251HE5Q78DR",
            "stage_type": "LEGACY_SHIPMENT",
            "directly_dependent_stages": [],
            "directly_depends_on_stages": [],
            "legacy_shipment_id": "b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e",
            "legacy_shipment": {
                "id": "b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e",
                "resource_type": "shipment",
                "self_link": "https://sandbox-api.shipwell.com/v2/shipments/b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e/"
            }
        }
    ]
}

Add a transload stage

Adding a transload stage is a multi-step process. First getting a list of service providers, then create a transload service, and finally assign the created transload service to a multi-stage shipment stage while defining the stage’s dependency.

Get a list of service providers

To get a list of potential service providers use the GET /v2/service-providers route to identify the id of the desired service provider with capabilities: ["TRANSLOAD"]. Here is an abbreviated example of the response for a service provider:

Example request
curljavascriptpythonjava
Copy
Copied
curl --location --request GET 'https://sandbox-api.shipwell.com/v2/service-providers' \
--header 'Authorization: YOUR_AUTHORIZATION_HEADER'
Copy
Copied
const basePath = "/v2";
const hostPath = "sandbox-api.shipwell.com";
const resp = await fetch(
  `https://${hostPath}${basePath}/shipments/service-providers`,
  {
    method: "GET",
    headers: {
      "Authorization": "YOUR_AUTHORIZATION_HEADER"
    }
  }
);

const data = await resp.json();
console.log(data);
Copy
Copied
import requests

base_path = "/v2"
host_path = "sandbox-api.shipwell.com"
target_url = (
    "https://"
    + host_path
    + base_path
    + "/service-providers"
)

headers = {"Authorization": "YOUR_AUTHORIZATION_HEADER"}

response = requests.get(target_url, headers=headers)
data = response.json()
print(data)
Copy
Copied
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

OkHttpClient client = new OkHttpClient();

String basePath = "/v2";
String hostPath = "sandbox-api.shipwell.com";
String targetUrl = "https://" +
  host_path +
  base_path +
  "/service-providers";

Request request = new Request.Builder()
  .url(targetUrl)
  .header("Authorization", "YOUR_AUTHORIZATION_HEADER")
  .build();

try (Response response = client.newCall(request).execute()) {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
    response.body().string();
}
Example response
Copy
Copied
{
   "id":"01GCFAZ41VG6DC08F6CZEN4P5P",
   "created_at":"2022-09-08T19:59:25.755080Z",
   "updated_at":"2022-09-08T19:59:25.756034Z",
   "name":"Brent Testing",
   "mailing_address":{
      "id":"4fcdb651-f0d9-4111-a333-8b980e463187",
      "address_1":"8901 Brook Rd",
      "address_2":null,
      "city":"Glen Allen",
      "state_province":"VA",
      "postal_code":"23060",
      "country":"US",
      "phone_number":null,
      "formatted_address":"8901 Brook Rd, Glen Allen, VA 23060, US",
      "created_at":"2022-09-08T19:59:25.461200Z",
      "updated_at":"2022-09-08T19:59:25.602573Z"
   },
   "tags":[
      
   ],
   "capabilities":[
      "TRANSLOAD"
   ],
   "notes":null,
   "billing_email":null,
   "status":"ACTIVE",
   "resource_type":"service-provider",
   "self_link":"http://sandbox-api.shipwell.com/v2/service-providers/01GCFAZ41VG6DC08F6CZEN4P5P"
}

Create a transload service

Use the id in the response body from the POST /shipments route we started with as the shipment_id and use the id from the POST /services/transload above as the service_id for the POST /shipments/{shipment_id}/services/{service_id} route to associate the service with the multi-stage shipment. In the request body below the dependency that will be associated is defined. We'll make this transload service dependent on the legacy shipment stage created earlier. Note the directly_depends_on_stages below references the id from the legacy shipment stage.

Example request
curljavascriptpythonjava
Copy
Copied
curl --location --request POST 'https://sandbox-api.shipwell.com/shipments/{shipment_id}/services/{service_id}' \
--header 'Authorization: YOUR_AUTHORIZATION_HEADER' \
--form 'data="{
    \"directly_depends_on_stages\": [\"01GKA2E6TFS7KPYJZPK6T4B5GY\"]
}"'
Copy
Copied
const payload = {
    "directly_depends_on_stages": ["01GKA2E6TFS7KPYJZPK6T4B5GY"]
};

const shipmentId = "YOUR_shipmentId_PARAMETER";
const serviceId = "YOUR_serviceId_PARAMETER";
const basePath = "";
const hostPath = "sandbox-api.shipwell.com";
const resp = await fetch(
  `https://${hostPath}${basePath}/shipments/${shipmentId}/services/${serviceId}`,
  {
    method: "POST",
    headers: {
      "Authorization": "YOUR_AUTHORIZATION_HEADER"
    },
    body: JSON.stringify(payload)
  }
);

const data = await resp.json();
console.log(data);
Copy
Copied
import requests

shipment_id = "YOUR_shipmentId_PARAMETER"
service_id = "YOUR_serviceId_PARAMETER"
base_path = ""
host_path = "sandbox-api.shipwell.com"
target_url = (
    "https://"
    + host_path
    + base_path
    + "/shipments/"
    + shipment_id
    + "/services/"
    + service_id
)

headers = {
    "Authorization": "YOUR_AUTHORIZATION_HEADER",
    "Content-Type": "application/json",
}

payload = {
    "directly_depends_on_stages": ["01GKA2E6TFS7KPYJZPK6T4B5GY"]
}

response = requests.post(target_url, headers=headers, json=payload)
data = response.json()
print(data)
Copy
Copied
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

OkHttpClient client = new OkHttpClient();
String shipmentId = "YOUR_shipmentId_PARAMETER";
String serviceId = "YOUR_serviceId_PARAMETER";
String basePath = "";
String hostPath = "sandbox-api.shipwell.com";
String targetUrl = "https://" +
  host_path +
  base_path +
  "/shipments/" +
  shipmentId +
  "/services/" +
  serviceId;

String requestBody = "{\"directly_depends_on_stages\": [\"01GKA2E6TFS7KPYJZPK6T4B5GY\"]}";

Request request = new Request.Builder()
  .url(targetUrl)
  .put(requestBody)
  .header("Authorization", "YOUR_AUTHORIZATION_HEADER")
  .header("Content-Type", "application/json")
  .build();

try (Response response = client.newCall(request).execute()) {
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  response.body().string();
}
Example response
Copy
Copied
Returns an empty response with a HTTP 204 (No Content) status code on success

Make another call to the GET /shipments/{shipment_id)/stages to confirm the new transload stage is listed.

Example request
curljavascriptpythonjava
Copy
Copied
curl --location --request GET 'https://sandbox-api.shipwell.com/shipments/{shipment_id}/stages' \
--header 'Authorization: YOUR_AUTHORIZATION_HEADER'
Copy
Copied
const shipmentId = "YOUR_shipmentId_PARAMETER"
const basePath = "";
const hostPath = "sandbox-api.shipwell.com";
const resp = await fetch(
  `https://${hostPath}${basePath}/shipments/${shipmentId}/stages`,
  {
    method: "GET",
    headers: {
      "Authorization": "YOUR_AUTHORIZATION_HEADER"
    }
  }
);

const data = await resp.json();
console.log(data);
Copy
Copied
import requests

shipment_id = "YOUR_shipmentId_PARAMETER"
base_path = ""
host_path = "sandbox-api.shipwell.com"
target_url = (
    "https://"
    + host_path
    + base_path
    + "/shipments/"
    + shipment_id
    + "/stages"
)

headers = {"Authorization": "YOUR_AUTHORIZATION_HEADER"}

response = requests.get(target_url, headers=headers)
data = response.json()
print(data)
Copy
Copied
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

OkHttpClient client = new OkHttpClient();

String shipmentId = "YOUR_shipmentId_PARAMETER";
String basePath = "";
String hostPath = "sandbox-api.shipwell.com";
String targetUrl = "https://" +
  host_path +
  base_path +
  "/shipments/" + 
  shipmentId + 
  "/stages";

Request request = new Request.Builder()
  .url(targetUrl)
  .header("Authorization", "YOUR_AUTHORIZATION_HEADER")
  .build();

try (Response response = client.newCall(request).execute()) {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
    response.body().string();
}
Example response
Copy
Copied
[
   {
      "id":"01GKA2E6TFS7KPYJZPK6T4B5GY",
      "shipment_id":"01GKA255FRSJ7Y1251HE5Q78DR",
      "stage_type":"LEGACY_SHIPMENT",
      "directly_dependent_stages":[
         "01GKAATMFA9A2XHW8CE334CPE0"
      ],
      "directly_depends_on_stages":[
         
      ],
      "legacy_shipment_id":"b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e",
      "legacy_shipment":{
         "id":"b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e",
         "resource_type":"shipment",
         "self_link":"https://sandbox-api.shipwell.com/v2/shipments/b901aaa2-e9e9-4621-8de8-89f8e5ceaa2e/"
      }
   },
   {
      "id":"01GKAATMFA9A2XHW8CE334CPE0",
      "shipment_id":"01GKA255FRSJ7Y1251HE5Q78DR",
      "stage_type":"SERVICE",
      "directly_dependent_stages":[
         
      ],
      "directly_depends_on_stages":[
         "01GKA2E6TFS7KPYJZPK6T4B5GY"
      ],
      "service":{
         "service_type":"TRANSLOAD",
         "reference_id":"JENHSG",
         "custom_data":null,
         "location":{
            "address":{
               "country":"US",
               "line_1":"8901 Three Chopt Rd",
               "line_2":null,
               "line_3":null,
               "locality":"Richmond",
               "postal_code":"23229",
               "region":"VA",
               "urbanization":null,
               "residential":false,
               "geolocation":null
            },
            "location_name":"Brent Testing",
            "location_number":"PA-1",
            "system_id":"Brent Testing, PA-1",
            "company_name":"Brent Testing",
            "location_type":"OTHER",
            "timezone":"America/Chicago"
         },
         "service_provider":{
            "provider_id":"01GESC9P9J5V0YW33JW7647Z6N",
            "identification_codes":[
               
            ],
            "name":"Brent Testing"
         },
         "actual_datetimes":null,
         "provider_rate":null,
         "customer_rate":null,
         "references":[
            
         ],
         "id":"01GKAASHXVS1ZKVR90RH7BAFJY"
      }
   }
]

Adding the full truck load stage

The final full truckload stage is added the same way the drayage stage was added to the transportation plan in the fist step of this developer guide and is left as an exercise for the developer to complete this example.

Copyright © Shipwell 2024. All right reserved.