1. Shipments
  2. Create Shipments

Shipments

Create Shipments

Creating a shipment is a two-step process. First, you will get shipping rates. Then you'll select one of those shipping rates to purchase.

Get Rates

POST
`/v1/shipments`

There are three pieces of information we require to get rates: sender, recipient, and parcel. There are also some additional options you can pass in to configure the shipment.

Sender & Recipient

The sender and recipient information have the same properties, so we'll cover both here.

js
        const sender = {
  name: "Jamie Jones",
  email: "jamie@packagex.io",
  phone: "4844836699",
  address: "500 7th ave, New York, NY, 10018",
};

      

We'll automatically parse the address for you. If you use an autocomplete like from Google Maps, Algolia, or PackageX, the format will come packed as a single string.

However, many autocomplete results don't come with a unit field, so typically that information is entered in a different input. If that is the case, you can provide a unit or apartment number in the address_line2 property.

js
        const recipient = {
  name: "Jamie Jones",
  email: "jamie@packagex.io",
  phone: "4844836699",
  address: "500 7th ave, New York, NY, 10018",
  address_line2: "Floor 10",
};

      

If you have the address information from the customer from multiple input fields, you can pass it through as an object instead of an inline string.

js
        const sender = {
  name: "Jamie Jones",
  email: "jamie@packagex.io",
  phone: "4844836699",
  address: {
    line1: "Jamie Jones",
    line2: null, //If there is no unit number
    city: "New York",
    state: "NY",
    postal_code: "10018",
  },
};

      

Lastly, if you already have reference to an address that we parsed before, you can pass that address's ID property instead. This will speed up your request.

js
        const sender = {
  name: "Jamie Jones",
  email: "jamie@packagex.io",
  phone: "4844836699",
  address: "addr_11fkZ8wQzscUj9SZaX1By1",
};

      

Parcel

For the parcel, you'll need to let us know the size and weight of the parcel. If you are using one of the predefined packages, you can reference that using the type field or you can enter the length, width, and height in inches. The weight is entered in lbs.

js
        const parcel = {
  length: 5,
  width: 1.4,
  height: 10,
  weight: 2,
};

      

You don't need to provide dimensions if using a predefined package. In fact, you don't strictly need to provide the weight either, but there are situations like with on-demand providers where not defaulting to the predefined package's max weight will result in better rates, so include this value if you have it.

js
        const parcel = {
  type: "usps_small_flat_rate_box",
  weight: 2, //This is optional for predefined packages too
};

      

You can also optionally provide a special_handing. The special_handling field is currently experimental and will be standardized soon.

js
        const parcel = {
  type: "usps_small_flat_rate_box",
  weight: 2,
  special_handling: "TIME SENSITIVE", //experimental
};

      

If you want to let us know about specific inventory you are shipping, you can also pass an Inventory array to describe the items that are inside of the parcel. This is done automatically for you if you are creating a shipment via a fulfillment_id, but you can do so manually if you would like. The reasons that you would want to include the inventory items in each parcel are:

js
        const parcel = {
  type: "usps_small_flat_rate_box",
  weight: 2,
  special_handling: "TIME SENSITIVE", //experimental
  inventory: [
    {
      name: "Dispatch Roasters Tequila",
      attributes: ["alcohol"],
    },
    {
      name: "Dispatch Roasters Coffee",
      vendor: "Dispatch, Inc",
      origin_country: "Ethiopia",
    },
  ],
};

      
  • you are shipping something internationally and need to include customs information
  • something you are shipping in this parcel needs additional care due to the item attributes like alcohol, hazardous materials, or pharmaceuticals.
  • you want to include additional information for your records later

Type

Typically, the shipment is always outbound but you have the option of creating an inbound shipment by specifying whether it is inbound or outbound by adding the following optional property in the request body (defaults to outbound):

  • type "outbound" | "inbound"

Options

Any omitted options will be defaulted to the values set in your settings.

Prop Type Default Description
verify_address boolean false If you want the sender and recipient addresses to be verified.
provider_timeout integer 15000 The maximum time in milliseconds you want to wait for a shipping provider to respond. If not responded, we'll return shipping labels from all providers who did respond on time.
checkout_total integer 0 The total value of the user's cart during ecommerce. This can manipulate shipping prices if any discounts have been configured.
request_provider_pickup boolean false If you want provider to specifically retrieve your package from you. This will result in a higher price for the rate. This should not be confused with a routine building pickup by a provider. This option is for providers who don't normally pickup your package directly to do so.
providers Array.<string> null You can limit the providers that you would like to see in the search results by passing in their ID. See providers for more information. A falsy value will reset the default values from your settings.
service_levels Array.<string> null You can limit the rates returned by specifying the service levels you'd like to have. See service_levels for more information on the IDs that can be passed in. A falsy value will reset the default values from your settings.
max_delivery_days integer null Add the maximum days from now when this shipment should be delivered. This is a good way to filter options from multiple providers without specifying specific rate classes.
rate_types Array.<string> [delivery] The types of rates you want returned. Options include: delivery, pickup. By default only delivery rates are shown, but when creating a shipment by passing a fulfillment_id, we'll include pickup rates as well.
chained_status TrackingStatusOutstanding [package_at_waypoint] on_provider_updates: Outstanding shipments will be closed only when the provider sends a tracking update to the shipment. on_all_updates: Outstanding shipments will be closed on any tracking update to the shipment off: Outstanding shipments will be ignored
auto_close_shipments_option TrackerCloseShipmentsOption [on_provider_updates] This option determines how to handle outstanding shipments
auto_close_shipments_status TrackerCloseShipmentsStatus [package_at_waypoint] When on_all_updates or on_provider_updates is active, you can select the following completed non-exception statuses for outstanding shipments
        enum TrackingStatusOutstanding {
  provider_at_pickup
  package_accepted
  out_for_delivery
  provider_at_dropoff
  scheduled
  driver_dispatched
  package_at_waypoint
  in_transit
  pickup_available
  recipient_at_pickup
  added_to_container
  removed_from_container
  added_to_vehicle
  removed_from_vehicle
  delivered_to_provider
}

      
        enum TrackerCloseShipmentsOption {
  on_provider_updates
  on_all_updates
  off
}

      
        enum TrackerCloseShipmentsStatus {
  delivered
  picked_up
}

      

Simple Options Example

js
        const parcel = {
  type: "usps_small_flat_rate_box",
  weight: 2,
  special_handling: "TIME SENSITIVE", //experimental
  inventory: [
    {
      name: "Dispatch Roasters Tequila",
      attributes: ["alcohol"],
    },
    {
      name: "Dispatch Roasters Coffee",
      vendor: "Dispatch, Inc",
      origin_country: "Ethiopia",
    },
  ],
};

const data = {
  sender: {
    name: "Jamie Jones",
    email: "jamie@packagex.io",
    phone: "4844836699",
    address: "500 7th ave, New York, NY, 10018",
    address_line2: "Floor 10",
  },
  recipient: {
    name: "Ashley Young",
    email: "ashley@packagex.io",
    phone: "4844836699",
    address: "3 Brewster Rd, Newark, NJ, 07114",
  },
  parcels: [parcel],
  options: {
    verify_address: false,
    provider_timeout: 5000,
    tracker_chained_status: "in_transit",
    tracker_auto_close_shipments_option: "on_all_updates",
    tracker_auto_close_shipments_status: "picked_up",
  },
};

const response = await fetch("https://api.packagex.io/v1/shipments", {
  method: "POST",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(data),
}).then((res) => res.json());

const shipment = response.data;

      

Filter Results Example

In the example below, we're showing all of the filter properties that you can add. It's not advise to add all of those options because there can be conflicts with the filtering resulting in all of the rates being filtered out.

INFO

If all of the shipping rates get filtered out, then all of the rates will be returned besides those that are offline_delivery to prevent accidentally leaking them to customers.

js
        const data = {
  sender: {
    name: "Jamie Jones",
    email: "jamie@packagex.io",
    phone: "4844836699",
    address: "500 7th ave, New York, NY, 10018",
    address_line2: "Floor 10",
  },
  recipient: {
    name: "Ashley Young",
    email: "ashley@packagex.io",
    phone: "4844836699",
    address: "3 Brewster Rd, Newark, NJ, 07114",
  },
  parcels: [
    {
      length: 5,
      width: 1.4,
      height: 10,
      weight: 2,
      item_description: "Candle",
      special_handling: "TIME SENSITIVE",
    },
  ],
  options: {
    verify_address: false,
    provider_timeout: 5000,
    request_provider_pickup: true,
    providers: ["marketplace", "usps", "doordash", "self"],
    service_levels: ["on_demand", "same_day", "ground"],
    max_delivery_days: 1,
    rate_types: ["delivery", "pickup"],
  },
};

const response = await fetch("https://api.packagex.io/v1/shipments", {
  method: "POST",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(data),
});

const shipment = response.data;

      

Multi-piece shipments

If shipping multiple boxes that are part of the same shipment, you can pass all of the parcels in an array. Each rate that is returned will have their price reflect both parcels.

Depending on the provider, someones both parcels will have the same tracking number while other times each parcel has a unique tracking number. This is dependent on how the provider does things. If the provider creates multiple tracking numbers, we'll add those tracking numbers to the tracking_number property of the parcel and use a single tracking number for the shipment's tracking_number property.

js
        const data = {
  sender: {
    name: "Jamie Jones",
    email: "jamie@packagex.io",
    phone: "4844836699",
    address: "500 7th ave, New York, NY, 10018",
    address_line2: "Floor 10",
  },
  recipient: {
    name: "Ashley Young",
    email: "ashley@packagex.io",
    phone: "4844836699",
    address: "3 Brewster Rd, Newark, NJ, 07114",
  },
  parcels: [
    {
      length: 5,
      width: 1.4,
      height: 10,
      weight: 2,
      item_description: "Candle",
      special_handling: "TIME SENSITIVE",
    },
  ],
  options: {
    verify_address: false,
    provider_timeout: 5000,
    request_provider_pickup: true,
    providers: ["marketplace", "usps", "doordash", "self"],
    service_levels: ["on_demand", "same_day", "ground"],
    max_delivery_days: 1,
    rate_types: ["delivery", "pickup"],
  },
};

fetch("https://api.packagex.io/v1/shipments", {
  method: "POST",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(data),
});

      

Refetch Rates

POST
`/v1/shipments/:shipment/refresh`

After you already fetched rates, you can also fetch new rates. You'll want to do this if:

  • Your rate has expired because the rates_generated_at is more than 15 minutes from now.
  • You want to update your options that you added into the shipment.

All of the options that are passed in for getting rates can also be passed in to refetch rates.

Purchase Rate

POST
`/v1/shipments/:shipment/purchase`

Properties

Now that you have generated your shipping rates, you are able to purchase the shipment. You'll want to find the id of the shipping rate to purchase from the response that you received from generating the shipping rates.

Prop Type Default Description
rate_id* string null The ID of the rate you want to purchase
payment_method string null We'll automatically bill the default payment method on your account if you don't provide a different option here
payment_reference string null You can add a payment reference here that will appear on your invoice, like a department ID or customer ID
label_size enum null You can add one of the label_size enums, otherwise we'll use the default value that is set on your dashboard

Example

In this instance, we'll select the cheapest shipping rate from the response we received in the previous step. To purchase the shipment, we'll need the shipment ID from the response and the rate ID that we want to purchase. Everything else is optional.

js
        const shipment = response.data; //the shipment is in the data property of the response object
const sorted_rate = shipment.rates.sort((a, b) => a.billed_amount - b.billed_amount);
const selected_rate = sorted[0];

const shipment_id = shipment.id;
const rate_id = selected_rate.id;

const body = {
  rate_id: rate_id,
  payment_reference: "Example",
  label_size: "4x6",
};

const response = await fetch(`https://api.packagex.io/v1/shipments/${shipment.id}/purchase`, {
  method: "POST",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
});

const shipment = response.data;

      

Select Payment Method Example

In this instance, we'll select the cheapest shipping rate from the response we received in the previous step. To purchase the shipment, we'll need the shipment ID from the response and the rate ID that we want to purchase. Everything else is optional.

js
        const payment_methods_response = await fetch(`https://api.packagex.io/v1/payment-methods`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
}).then(res.json());

const payment_method_id = payment_methods_response.data[1].id; //Pick one that you want

const shipment = response.data; //the shipment is in the data property of the response object
const sorted_rate = shipment.rates.sort((a, b) => a.billed_amount - b.billed_amount);
const selected_rate = sorted[0];

const shipment_id = shipment.id;
const rate_id = selected_rate.id;

const body = {
  rate_id: rate_id,
  payment_method: payment_method_id,
};

fetch(`https://api.packagex.io/v1/shipments/${shipment.id}/purchase`, {
  method: "POST",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
});

      

Complete Example

js
        const data = {
  sender: {
    name: "Jamie Jones",
    email: "jamie@packagex.io",
    phone: "4844836699",
    address: "500 7th ave, New York, NY, 10018",
    address_line2: "Floor 10",
  },
  recipient: {
    name: "Ashley Young",
    email: "ashley@packagex.io",
    phone: "4844836699",
    address: "3 Brewster Rd, Newark, NJ, 07114",
  },
  parcels: [
    {
      length: 5,
      width: 1.4,
      height: 10,
      weight: 2,
      special_handling: "TIME SENSITIVE", //experimental
      inventory: [
        {
          name: "Dispatch Roasters Tequila",
          attributes: ["alcohol"],
        },
        {
          name: "Dispatch Roasters Coffee",
          vendor: "Dispatch, Inc",
          origin_country: "Ethiopia",
        },
      ],
    },
  ],
  options: {
    verify_address: false,
    provider_timeout: 5000,
    max_delivery_days: 3, //Basically filter's out ground shipping
  },
};

try {
  let res = fetch("https://api.packagex.io/v1/shipments", {
    method: "POST",
    headers: {
      "PX-API-KEY": process.env.PX_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(data),
  }).then((res) => res.json());

  const shipment = res.data; //the shipment is in the data property of the response object
  const sorted_rate = shipment.rates.sort((a, b) => a.billed_amount - b.billed_amount);
  const selected_rate = sorted[0]; //Get the cheapest rate

  const body = {
    rate_id: selected_rate.id,
    payment_reference: "Example",
    label_size: "4x6",
  };

  res = await fetch(`https://api.packagex.io/v1/shipments/${shipment.id}/purchase`, {
    method: "POST",
    headers: {
      "PX-API-KEY": process.env.PX_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  }).then((res) => res.json());

  const shipment = res.data;
  const label_url = shipment.label_url;
} catch (err) {
  console.error(err);
}