Fulfill an Order

This guide walks you through the complete process of fulfilling orders using the ShipOS API. The fulfillment process involves multiple steps to ensure data integrity and proper inventory management.

Prerequisites

  • A valid API key with fulfillment permissions
  • An order ID (we'll show you how to get the fulfillment order ID from this)
  • A shipping label to upload OR a carrier integration setup/linked for rate shop

Understanding Order IDs vs Fulfillment Order IDs

Important: The order_id and fulfillment_order_id are different!

  • Order ID: The main order identifier (e.g., ord_123abc...)
  • Fulfillment Order ID: A sub-order for fulfillment purposes (e.g., fo_456def...)

One order can have multiple fulfillment orders if items are fulfilled separately or split across warehouses.

Overview

The complete fulfillment process follows these steps:

  1. Get fulfillment orders from an order - Find the fulfillment order ID(s) from your order ID
  2. Acquire a lock on the fulfillment order - Prevent concurrent fulfillment
  3. Retrieve fulfillment order details - Get shipping and line item information
  4. Create one or more packages - Define what you're shipping
  5. Upload or generate shipping labels - Add tracking information
  6. Fulfill the package(s) - Complete the fulfillment

After successful fulfillment, the lock will be automatically released. If you no longer wish to fulfill this fulfillment order, we recommend releasing the lock to allow other fulfillment methods (Picking Sessions, Bulk Ship, Manual Fulfillments) to fulfill this fulfillment order. The lock will also expire automatically if not released.

Step 1: Get Fulfillment Orders

First, confirm the order exists and note its order_id:

GET /v1/shipos/orders/{order_id}
{
  "id": "ord_123abc...",
  "status": "PENDING_FULFILLMENT",
}

Then, request the fulfillment orders for that order by passing the order_id filter to the Get many fulfillment orders endpoint:

GET /v1/shipos/fulfillment-orders?order_id={order_id}
{
  "data": [
    {
      "id": "fo_456def...",
      "order_id": "ord_123abc...",
      "status": "IN_PROGRESS",
      "request_status": "ACCEPTED"
    }
  ],
  "meta": {
    "page": 0,
    "page_size": 20,
    "total": 1
  }
}

Key points:

  • The Get Order response does not embed fulfillment orders; always call GET /v1/shipos/fulfillment-orders with an order_id filter to fetch them.
  • Fulfillment orders are returned in the data array; use each id (e.g., fo_456def...) in the remaining steps.
  • An order may have multiple fulfillment orders if items are split across warehouses.
  • Only fulfillment orders with status ACCEPTED or IN_PROGRESS can be locked.

Before proceeding: Ensure the fulfillment order request_status is ACCEPTED and the status is IN_PROGRESS.

Step 2: Acquire Lock

First, acquire an exclusive lock on the fulfillment order to prevent double fulfillment scenarios:

POST /v1/shipos/fulfillment-orders/{fulfillment_order_id}/lock

Request:

{
  "lock_owner": "lock_holder",
  "timeout_minutes": 30  // 30 minutes
}

Response:

{
  "success": true,
  "lock_owner": "lock_holder",
  "expires_at": "2024-01-20T10:15:00Z"
}

The lock will automatically expire after the timeout period if not released manually.

Step 3: Get Fulfillment Order Details

Retrieve the complete fulfillment order details:

GET /v1/shipos/fulfillment-orders/{fulfillment_order_id}

Response includes:

  • Line items with quantities to fulfill
  • Shipping address information
  • Current fulfillment status

Step 4: Create Package

Create one or more packages for the items you're shipping:

POST /v1/shipos/packages

Request:

{
  "fulfillment_order_id": "fo_123abc...",
  "name": "Box #1",
  "weight": 2.5,
  "weight_unit": "LB",
  "length": 12,
  "width": 10,
  "height": 8,
  "measurement_unit": "IN",
  "rate_strategy": "CHEAPEST",
  "carton_id": "carton_123...",
  "package_line_items": [
    {
      "sku": "PRODUCT-001",
      "quantity": 2,
      "product_id": "prod_123..."
    },
    {
      "sku": "PRODUCT-002",
      "quantity": 1,
      "product_id": "prod_456..."
    }
  ]
}

Response:

{
  "id": "pkg_789xyz...",
  "status": "PENDING",
  "tracking_number": null,
  // ... other package details
}

Partial Fulfillment

You can create multiple packages or fulfill partial quantities:

{
  "package_line_items": [
    {
      "sku": "PRODUCT-001",
      "quantity": 1  // Only shipping 1 of 2 ordered
    }
  ]
}

Step 5: Upload or Generate Shipping Label

Upload or Generate shipping labels for packages:

POST /v1/shipos/packages/{package_id}/label

Request:

{
  "label_url": "https://your-label-storage.com/labels/label_123.pdf",
  "tracking_number": "1Z999AA10123456784",
  "carrier": "UPS",
  "service": "GROUND",
  "cost": 12.50,
  "cost_currency": "USD"
}

You can generate labels through ShipOS's integrated carriers or upload labels generated externally.

Step 6: Fulfill Package

Fulfill the packages, which will bill the merchant (if applicable), push tracking to the sales channel, decrement remaining quantity on line items.

POST /v1/shipos/packages/{package_id}/fulfill

Request:

{
  "locked_by": "lock_holder"
}

To learn more about the side effects of the fulfill operation, please visit the fulfill endpoint documentation

Next Steps