> ## Documentation Index
> Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Fulfillment

> Create a fulfillment for order items and automatically mark the order as shipped

Creates a fulfillment record on the order and, when `status` is a
terminal success state, **automatically bumps `order.status` to
`shipped`** (or `delivered`). Shipping apps don't need a second call to
update the order — one POST writes the tracking number, courier, and
status in a single request.

<Note>
  This endpoint is the **write-back** half of the live-rate flow. For
  the full lifecycle (quote → checkout pick → orders/create webhook →
  push to carrier → call this endpoint with the AWB), see
  [Live Rate Providers](/extensions/live-rate-providers) and
  [Build a shipping app](/guides/shipping-app-integration).
</Note>

## When the order status changes

Each Fulfillment row represents **one shipment**. After every write, the
platform recomputes coverage by summing `lineItems[].quantity` across
all success-state Fulfillment rows for the order and updates
`order.status` accordingly:

| Coverage                              | `order.status` becomes                                              |
| ------------------------------------- | ------------------------------------------------------------------- |
| 0% — no success fulfillments yet      | unchanged                                                           |
| > 0% but \< 100% — some packages out  | `partial`                                                           |
| 100% — every ordered quantity shipped | `shipped` (or `delivered` if the fulfillment status is `delivered`) |

The order lifecycle is:

```
pending → confirmed → paid → partial → shipped → delivered
                                              (canceled / abandoned as terminal off-ramps)
```

`order.status` is only mutated from `pending`, `confirmed`, `paid`, or
`partial`. Manual overrides (`shipped` set by the merchant) and
terminal states (`canceled`/`delivered`) are never trampled.

### Multi-package orders

For orders that ship in multiple packages, POST one fulfillment per
shipment and include only the items in that package in
`line_items`. The platform tracks coverage per `line_item.id`:

```bash theme={null}
# Shipment 1 — 2 of the 5 items go out today
curl -X POST '.../orders/ord_xyz789/fulfillments.json' \
  -d '{
    "status": "success",
    "tracking_number": "AWB-PKG-A",
    "tracking_company": "DHL",
    "tracking_url": "https://dhl.com/track/AWB-PKG-A",
    "line_items": [
      { "id": "li_001", "quantity": 1 },
      { "id": "li_002", "quantity": 1 }
    ]
  }'
# → order.status flips from "paid" → "partial"

# Shipment 2 — remaining 3 items ship a day later
curl -X POST '.../orders/ord_xyz789/fulfillments.json' \
  -d '{
    "status": "success",
    "tracking_number": "AWB-PKG-B",
    "tracking_company": "FedEx",
    "tracking_url": "https://fedex.com/track/AWB-PKG-B",
    "line_items": [
      { "id": "li_003", "quantity": 1 },
      { "id": "li_004", "quantity": 1 },
      { "id": "li_005", "quantity": 1 }
    ]
  }'
# → order.status flips from "partial" → "shipped"
```

Omitting `line_items` is the back-compat shortcut for single-shipment
orders: the fulfillment is treated as covering everything, so a single
POST flips `order.status` straight to `shipped` (skipping `partial`).

## Path Parameters

<ParamField path="order_id" type="string" required>
  The order ID
</ParamField>

## Body Parameters

<ParamField body="status" type="string" default="pending">
  Initial fulfillment status. Set to `success` to immediately mark the
  shipment as out the door — this is what bumps `order.status` to
  `shipped`. Allowed: `pending`, `open`, `success`, `cancelled`,
  `error`, `failure`.
</ParamField>

<ParamField body="tracking_number" type="string">
  Carrier tracking number (e.g. AWB code).
</ParamField>

<ParamField body="tracking_company" type="string">
  Carrier name (e.g. `"UPS"`, `"FedEx"`, `"USPS"`, `"DHL"`,
  `"Shiprocket"`).
</ParamField>

<ParamField body="tracking_url" type="string">
  Public, shopper-facing tracking URL. Must be a valid URL.
</ParamField>

<ParamField body="service_id" type="string">
  Optional. The ID of the fulfillment service that produced this
  shipment, returned by [Create Fulfillment Service](/api-reference/fulfillment-services/create).
  Caching this on a shop metafield after registration avoids a
  list-call on every push.
</ParamField>

<ParamField body="line_items" type="array">
  Optional. Items being fulfilled in this shipment. Omit for
  single-shipment orders where every line item ships together.

  <Expandable title="line item">
    <ParamField body="id" type="string" required>Line item ID</ParamField>
    <ParamField body="quantity" type="number" required>Quantity to fulfill</ParamField>
  </Expandable>
</ParamField>

<ParamField body="notify_customer" type="boolean" default="true">
  Send shipment notification email to the customer.
</ParamField>

## Side effects

When the call succeeds:

1. A fulfillment record is created on the order.
2. If `status` triggers a status flip (see table above), `order.status`
   is updated in the same request.
3. A `fulfillments/create` webhook is dispatched to every subscriber on
   the store (3-retry exponential backoff: 1m / 5m / 15m).

## Required scope

`write_orders`

## Request example

```bash theme={null}
curl -X POST 'https://api.launchmystore.io/api/v1/orders/ord_xyz789/fulfillments.json' \
  -H 'Authorization: Bearer <oauth_access_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "tracking_number": "1Z999AA10123456784",
    "tracking_company": "UPS",
    "tracking_url": "https://ups.com/track?num=1Z999AA10123456784",
    "status": "success",
    "service_id": "fs_abc123"
  }'
```

## Response

<ResponseExample>
  ```json theme={null}
  {
    "status": 201,
    "state": "success",
    "data": {
      "fulfillment": {
        "fulfillmentId": "ful_new456",
        "orderId": "ord_xyz789",
        "status": "success",
        "trackingNumber": "1Z999AA10123456784",
        "trackingCompany": "UPS",
        "trackingUrl": "https://ups.com/track?num=1Z999AA10123456784",
        "serviceId": "fs_abc123",
        "createdAt": "2024-01-20T14:30:00Z"
      }
    }
  }
  ```
</ResponseExample>

<Note>
  `order.status` is **not** returned in this response — the order row is
  updated as a side effect. Re-fetch the order via
  [Get Order](/api-reference/orders/get) if you need the new status in
  the same flow.
</Note>
