1. Inventory
  2. Fulfillments

Inventory

Fulfillments

Fulfillments are a multi-step process and this guide will go through all steps in detail. As a quick overview, the following are the steps required:

  1. Create fulfillment

    Add the required inventory that needs to be shipped to the desired recipient from your specified location.

  2. Create parcel

    Add the box or boxes into which you're going to pack the inventory items.

  3. Pack parcel

    Pack the items into the parcel(s) that you have added.

  4. Update to `packed`

    Mark the fulfillment as packed.

  5. Generate shipping rates

    Pass the fulfillment ID into the shipments API to get shipping rates for the parcel(s) you are shipping.

  6. Get shipping label

    Purchase a shipping label to completed the fulfillment. You can also set up private rates which do not require you to purchase a label if you are handling the shipping by yourself or sourcing a label off of the platform.

  7. !!!


    1. Create fulfillment

    POST `/v1/fulfillments`

    To create a fulfillment, you'll need to include the recipient, your location_id from where the fulfillment will be taking place, and the inventory that you want to include. Some optional extra params are in the code example below as well.

    There are two ways to go about working with inventory: either by using the inventory that is stored on Dispatch, or by bypassing it entirely if you are using a third party inventory solution. Both options are documented below.

    Example

    js
    const data = {
      location_id: "loc_afnHMjVUn3gnrvxU5zMvkX",
      recipient: {
        name: "Jamie Jones",
        email: "jamie@packagex.xyz",
        phone: "4844836699",
        address: "500 7th Ave, New York, NY 10018",
      },
      inventory: [
        //If the inventory item is all set up, just the ID and quantity you want to send is required
        {
          id: "item_5og9NZ92SYzHKFTPYSEnVK",
          verified_qty: 25,
        },
        //If the item was created but does not have the packaged_* dimensions, they can be entered inline
        {
          id: "item_1nCNpqtyepe48BMNhzmW54",
          verified_qty: 50,
          packaged_length: 2,
          packaged_width: 3,
          packaged_height: 4,
          packaged_weight: 6,
        },
        //If an item is not in our inventory system, it can be added without providing an ID. Ideally a SKU or GTIN is added for barcode scanning by the users.
        //A special, temporary ID will be generated for it with the prefix temp_ to make working with the data easier
        {
          name: "Custom Item",
          verified_qty: 5,
          packaged_length: 2,
          packaged_width: 3,
          packaged_height: 4,
          packaged_weight: 6,
          sku: "237489237489",
          image_url: "https://i.imgur.com/FKmX7dt.gif", //You can include an image as a nice-to-have for the packing slip
        },
      ],
    };
    
    const response = await fetch("https://api.packagex.io/v1/fulfillments", {
      method: "POST",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });
    
    const fulfillment = response.data;
    


    2. Create parcel

    POST `/v1/fulfillments/:fulfillment/parcels`

    Once the fulfillment has been created, it needs to be packed. But before that, one or more parcels need to be created. You can create a custom parcel by adding dimensions or by using a predefined package that you set up on the dashboard.

    While predefined packages have a maximum weight which is checked by the API, manually created packages do not have a weight check.

    Create custom parcel

    js
    //All dimensions are in inches. This will create a 10x10x10 box
    const data = {
      length: 10,
      width: 10,
      height: 10,
    };
    
    const response = await fetch(`https://api.packagex.io/v1/fulfillments/${fulfillment.id}/parcels`, {
      method: "POST",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });
    
    //We'll still respond with the fulfillment object here with your new parcel nested inside of it
    const fulfillment = response.data;
    

    Use predefined parcel

    js
    // fetch your org profile
    // the `/org` is an alias for `/organization/:org`
    // you have most likely already fetched your org profile at some point so you might already have it handy
    const response = await fetch(`https://api.packagex.io/v1/org`, {
      method: "GET",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    }).then(res.json());
    
    // breaking down things just to show details
    // all app settings are found on the org profile, here we find the fulfillment settings
    const fulfillment_settings = response.data.settings.fulfillments;
    
    // get the predefined packages
    const predefined_packages = fulfillment_settings.predefined_packages;
    
    // select the predefined package that you want according to your logic.
    const selected_package = predefined_packages[0];
    
    // pass the predefined package type as the type field. If this package is custom to you, it will status with pdp_
    // if you have used a carrier specific predefined package, this value will be that of the carrier's predefined package.
    const data = {
      type: selected_package.type, //pdp_qbpXmaGUYroknGjMaHsFcJ or usps_flat_fate_envelope
    };
    
    fetch(`https://api.packagex.io/v1/fulfillments/${fulfillment.id}/parcels`, {
      method: "POST",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });
    

    3. Pack parcel

    POST `/v1/fulfillments/:fulfillment/parcels/:parcel`

    Once the parcel for the fulfillment has been created, it is time to add items into it. You can specify adding items into the parcel the same way as when creating fulfillments or verifying manifests.

    Example

    js
    const data = {
      inventory: [
        {
          id: "item_1nCNpqtyepe48BMNhzmW54",
          verified_qty: 2,
        },
        {
          id: "temp_5og9NZ92SYzHKFTPYSEnVK", //If inventory is externally created
          verified_qty: 2,
          damaged_qty: 5,
        },
      ],
    };
    
    const response = await fetch(`https://api.packagex.io/v1/fulfillments/${fulfillment.id}/parcels/${parcel.id}`, {
      method: "POST",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });
    
    const fulfillment = response.data.data.data.data.data.data;
    

    Delete parcel

    DELETE `/v1/fulfillments/:fulfillment/parcels/:parcel`

    If you decide to delete the parcel from the fulfillment, any inventory attributed to that parcel will be removed and reset.

    Example

    js
    const response = await fetch(`https://api.packagex.io/v1/fulfillments/${fulfillment.id}/parcels/${parcel.id}`, {
      method: "DELETE",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
    });
    
    const fulfillment = response.data;
    

    4. Update to 'Packed'

    POST `/v1/fulfillments/:fulfillment`

    Once the fulfillment is packed to your satisfaction -- note that it does not need to be fully packed if you have partial fulfillments enabled via the dashboard settings or API -- you can update the status of the fulfillment to 'packed'.

    Of course, you can also update the status of the fulfillment to 'canceled' at any time in the same way.

    Example

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

    5. Generate shipping rates

    POST `/v1/shipments`

    Since we've collected all of the shipping information throughout the process of creating and updating the fulfillment and the parcels within, we are able to generate shipping rates by just passing the fulfillment ID to the shipping API.

    This will create a shipment ID on the fulfillment property, which you can use to retrieve the shipping rates and refresh the rates if the rates_generated_at timestamp is more than 15 minutes old.

    Get rates first time

    js
    const data = {
      fulfillment_id: "ful_hsvAifCQy8F8SJ6jSxxjsv",
    };
    
    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;
    

    Refresh rates

    If you need to refresh the shipping rates, make a call to the refresh endpoint with the shipment ID property on your fulfillment, no body needed.

    js
    const response = await fetch(`https://api.packagex.io/v1/shipments/${fulfillment.shipment_id}/refresh`, {
      method: "POST",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
    });
    
    const shipment = response.data;
    

    6. Generate shipping label

    POST `/v1/shipments/:shipment`

    Once you have found the shipping label that suits your needs, you can purchase it by passing the rate ID into the request body. There are additional options availible here, please see the shipments section.

    In the response, you'll have the label_url with the PDF of your shipping label(s). If there are multiple shipping labels, the label_url property will combine all shipping labels into one PDF document for easy printing. Each parcel in the shipment will also have its own PDF document attached to it.

    Example

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

    Retrieve Fulfillment

    GET `/v1/fulfillments/:fulfillment`

    Get a single fulfillment using its id.

    js
    const response = await fetch("https://api.packagex.io/v1/fulfillment/ful_czhgjrk5JaVvyATPDbyURp", {
      method: "GET",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
    });
    
    const fulfillment = response.data;
    

    List Fulfillments

    Example

    GET `/v1/fulfillments`

    When you want to retrieve multiple fulfillments, your data property on the result will always be an array even if you don't have any fulfillments.

    js
    const response = await fetch("https://api.packagex.io/v1/fulfillments", {
      method: "GET",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
    }).then((res) => res.json());
    
    const fulfillments = response.data; //the array of fulfillments
    const pagination = response.pagination; //the pagination object
    

    Pagination

    If the has_more property on the pagination object is set to true, you know there are more fulfillments in the database that have not been returned to you. The pagination object also has a page property indicating your current offset and a limit property.

    By default the page is set to 1 and the limit is 25.

    If we want to query for items 26 - 50, we would request page 2 with a query parameter.

    js
    const response = await fetch("https://api.packagex.io/v1/fulfillments?page=2&limit=25", {
      method: "GET",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
    }).then((res) => res.json());
    
    const fulfillments = response.data; //the array of items 25 - 50
    const pagination = response.pagination; //the pagination object
    

    Filter

    You can filter fulfillments by location.

    js
    const response = await fetch("https://api.packagex.io/v1/fulfillments?location=loc_hj7gjrk5JaVvyATPDbyURp&status=compelted", {
      method: "GET",
      headers: {
        "PX-API-KEY": process.env.PX_API_KEY,
        "Content-Type": "application/json",
      },
    }).then((res) => res.json());
    
    const fulfillments = response.data;
    const pagination = response.pagination;