Item Fulfillment Concepts

In this document, you’ll learn about the concepts related to item fulfillment.

Fulfillment Data Model#

A fulfillment is the shipping and delivery of one or more items to the customer. It’s represented by the Fulfillment data model.

A fulfillment can be created to fulfill orders, returns, exchanges, and claims.


Fulfillment Processing by a Fulfillment Provider#

A fulfillment is associated with a fulfillment provider that handles all its processing, such as creating a shipment for the fulfillment’s items.

The fulfillment is also associated with a shipping option of that provider, which determines how the item is shipped.

A diagram showcasing the relation between a fulfillment, fulfillment provider, and shipping option


data Property of Fulfillment Data Model#

The Fulfillment data model has a data property that holds any necessary data for the third-party fulfillment provider to process the fulfillment.

For example, the data property can hold the ID of the fulfillment in the third-party provider. The associated fulfillment provider then uses it whenever it retrieves the fulfillment's details.


Pass Additional Data to the Fulfillment Provider#

Note: This feature is available since Medusa v2.19.0.

When creating a fulfillment through the Create Fulfillment API route, you can pass an additional_data field in the request body. Unlike the data field, additional_data is not persisted on the fulfillment record; it is only forwarded as-is to the Fulfillment Module Provider's createFulfillment method.

This lets you send custom key-value pairs to the provider at creation time without storing them on the fulfillment. For example, you can pass a carrier-specific instruction that the provider reads when it talks to the third-party service:

Request body
1{2  "location_id": "loc_123",3  "items": [...],4  "additional_data": {5    "carrier_instruction": "leave_at_door"6  }7}

In the Fulfillment Module Provider's createFulfillment method, the fifth argument receives this object:

src/modules/my-fulfillment/service.ts
1class MyFulfillmentProviderService2  extends AbstractFulfillmentProviderService {3  // ...4
5  async createFulfillment(6    data: Record<string, unknown>,7    items: object[],8    order: object | undefined,9    fulfillment: Record<string, unknown>,10    additionalData?: Record<string, unknown>11  ) {12    const instruction = additionalData?.carrier_instruction13    // use instruction when calling the third-party service14  }15}

Override Delivery Address Per Fulfillment#

Note: This feature is available since Medusa v2.19.0.

By default, when creating a fulfillment for an order, Medusa uses the order's shipping address as the delivery address sent to the Fulfillment Module Provider.

You can override this on a per-fulfillment basis by passing a delivery_address field in the Create Fulfillment API route request body. The provided address is merged over the order's shipping address, so you only need to supply the fields you want to change:

Request body
1{2  "location_id": "loc_123",3  "items": [...],4  "delivery_address": {5    "first_name": "Jane",6    "last_name": "Doe"7  }8}

The merged address is forwarded to the Fulfillment Module Provider but the order's shipping address is not mutated.


Fulfillment Items#

A fulfillment is used to fulfill one or more items. Each item is represented by the FulfillmentItem data model.

The fulfillment item holds details relevant to fulfilling the item, such as barcode, SKU, and quantity to fulfill.

A diagram showcasing the relation between fulfillment and fulfillment items.


Fulfillment Label#

Once a shipment is created for the fulfillment, you can store its tracking number, URL, or other related details as a label, represented by the FulfillmentLabel data model.

Accessing Tracking Information#

To access tracking information for a fulfillment, you must retrieve the related labels. For example, to retrieve the tracking data with Query:

Code
1const { data: fulfillment } = query.graph({2  entity: "fulfillment",3  fields: [4    "labels.tracking_number",5    "labels.tracking_url",6    "labels.label_url",7  ],8  filters: {9    id: "fulfillment_123",10  },11})12
13fulfillment.labels.forEach((label) => {14  console.log("Tracking Number:", label.tracking_number)15  console.log("Tracking URL:", label.tracking_url)16  console.log("Label URL:", label.label_url)17})

Fulfillment Status#

The Fulfillment data model has three properties to determine the current status of the fulfillment:

  • packed_at: The date the fulfillment was packed. If set, the fulfillment has been packed.
  • shipped_at: The date the fulfillment was shipped. If set, the fulfillment has been shipped.
  • delivered_at: The date the fulfillment was delivered. If set, the fulfillment has been delivered.
Was this page helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Medusa MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break