> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.shipbob.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.shipbob.com/_mcp/server.

# Update Shipment Line Items by SKU


POST https://api.shipbob.com/2026-01/shipment/{shipmentId}:updateLineItemsBySku
Content-Type: application/json

Updates the line items for a specific shipment, identifying each item by SKU instead of inventory id. The SKU matches both the ShipBob variant SKU and the channel seller SKU. The request body must include the complete list of line items — partial updates are not supported. Retrieve the current line items first (via GET /shipment/\{shipmentId}:getLineItems, which returns each item's `product_variant.sku`), modify the desired fields, and submit the full payload.

**SKU resolution:** Resolution is all-or-nothing — if any SKU in the request cannot be resolved, the whole request fails and no update occurs. A SKU that resolves to a bundle or to multiple inventory items is rejected with a per-item error; use the inventory\_id-based endpoint (:updateLineItems) for those items.

**Important:** The `is_manually_assigned_lot` field must be set to the same value for all line items in the request. When set to `true` on any item, ShipBob treats all lot assignments in the shipment as manually reviewed — ShipBob will not auto-assign or re-optimize lots for any item. When set to `false`, ShipBob manages lot selection automatically. Mixing `true` and `false` across items in the same request will result in a validation error.

**Note on error handling:** This endpoint returns HTTP 200 for both successful and failed operations. Always check the `is_success` field in the response body to determine the outcome. When `is_success` is `false`, the `error` object contains the error code and message. HTTP 400 is only returned when the request body itself is missing or empty.

Reference: https://developer.shipbob.com/2026-01/api/orders/update-shipment-line-items-by-sku

## Authentication

- `Authorization` header (bearer token, required) — Authentication using Personal Access Token (PAT) token
- `Authorization` header (bearer token, required) — OAuth2 authentication using JWT tokens

## Servers

- `https://api.shipbob.com` (https://api.shipbob.com, default)
- `https://sandbox-api.shipbob.com` (https://sandbox-api.shipbob.com)

## Request

### Path parameters

- `shipmentId` (integer, required) — Unique identifier of the shipment

### Body (application/json)

- `items` (list of object, required) — Complete list of line items for the shipment, identified by SKU. Must include all line items — partial updates are not supported
  - `quantity` (integer, required) — Quantity of the inventory item in the shipment
  - `sku` (string, required) — SKU of the inventory item. Matches both the ShipBob variant SKU and the channel seller SKU. A SKU that resolves to a bundle or to multiple inventory items is rejected — use the inventory_id-based endpoint (:updateLineItems) for those.
  - `is_manually_assigned_lot` (boolean, optional) — Indicates whether the lot was manually assigned. When true, ShipBob treats all lot assignments in the shipment as manually reviewed. Must be the same value for all line items in the request.
  - `lot_date` (string, optional, nullable) — Expiration or manufacturing date of the lot
  - `lot_number` (string, optional, nullable) — Lot number of the inventory item

## Response

### 200

OK

- `error` (object, optional, nullable) — Error details if the update was not successful
  - `code` (enum, optional) — Error code identifying the type of error
    - Allowed values: `INVALID_PARAMETER`, `VALIDATION_ERROR`, `DATABASE_UPDATE_ERROR`, `REPROCESSING_ERROR`, `DEALLOCATE_ERROR`, `NOT_FOUND`, `CONFLICT`
  - `message` (string, optional, nullable) — Human-readable description of the error
- `id` (long, optional) — Unique identifier of the shipment
- `is_success` (boolean, optional) — Indicates whether the update was successful
- `shipment_line_items` (list of object, optional, nullable) — List of line item changes applied to the shipment
  - `action` (string, optional, nullable) — The action applied to this line item during the update
  - `inventory_id` (integer, optional) — Unique identifier of the inventory item
  - `new_value` (string, optional, nullable) — The new value of the field after the update
  - `previous_value` (string, optional, nullable) — The previous value of the field before the update

## Examples

**Request**

```json
{
  "items": [
    {
      "quantity": 5,
      "sku": "SKU-ABC-001",
      "is_manually_assigned_lot": true,
      "lot_date": "2024-06-01",
      "lot_number": "LOT-2024-001"
    },
    {
      "quantity": 1,
      "sku": "SKU-XYZ-002",
      "is_manually_assigned_lot": true,
      "lot_date": "2024-11-01",
      "lot_number": "LOT-2024-003"
    }
  ]
}
```

**Response**

```json
{
  "id": 476997333,
  "is_success": true,
  "shipment_line_items": [
    {
      "action": "ItemQtyUpdated",
      "inventory_id": 789012,
      "new_value": "5",
      "previous_value": "3"
    },
    {
      "action": "ItemAdded",
      "inventory_id": 345678,
      "new_value": "1",
      "previous_value": "0"
    }
  ]
}
```

**SDK Code**

```python default
import requests

url = "https://api.shipbob.com/2026-01/shipment/1:updateLineItemsBySku"

payload = { "items": [
        {
            "quantity": 5,
            "sku": "SKU-ABC-001",
            "is_manually_assigned_lot": True,
            "lot_date": "2024-06-01",
            "lot_number": "LOT-2024-001"
        },
        {
            "quantity": 1,
            "sku": "SKU-XYZ-002",
            "is_manually_assigned_lot": True,
            "lot_date": "2024-11-01",
            "lot_number": "LOT-2024-003"
        }
    ] }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript default
const url = 'https://api.shipbob.com/2026-01/shipment/1:updateLineItemsBySku';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"items":[{"quantity":5,"sku":"SKU-ABC-001","is_manually_assigned_lot":true,"lot_date":"2024-06-01","lot_number":"LOT-2024-001"},{"quantity":1,"sku":"SKU-XYZ-002","is_manually_assigned_lot":true,"lot_date":"2024-11-01","lot_number":"LOT-2024-003"}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go default
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.shipbob.com/2026-01/shipment/1:updateLineItemsBySku"

	payload := strings.NewReader("{\n  \"items\": [\n    {\n      \"quantity\": 5,\n      \"sku\": \"SKU-ABC-001\",\n      \"is_manually_assigned_lot\": true,\n      \"lot_date\": \"2024-06-01\",\n      \"lot_number\": \"LOT-2024-001\"\n    },\n    {\n      \"quantity\": 1,\n      \"sku\": \"SKU-XYZ-002\",\n      \"is_manually_assigned_lot\": true,\n      \"lot_date\": \"2024-11-01\",\n      \"lot_number\": \"LOT-2024-003\"\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby default
require 'uri'
require 'net/http'

url = URI("https://api.shipbob.com/2026-01/shipment/1:updateLineItemsBySku")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"items\": [\n    {\n      \"quantity\": 5,\n      \"sku\": \"SKU-ABC-001\",\n      \"is_manually_assigned_lot\": true,\n      \"lot_date\": \"2024-06-01\",\n      \"lot_number\": \"LOT-2024-001\"\n    },\n    {\n      \"quantity\": 1,\n      \"sku\": \"SKU-XYZ-002\",\n      \"is_manually_assigned_lot\": true,\n      \"lot_date\": \"2024-11-01\",\n      \"lot_number\": \"LOT-2024-003\"\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java default
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.shipbob.com/2026-01/shipment/1:updateLineItemsBySku")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"items\": [\n    {\n      \"quantity\": 5,\n      \"sku\": \"SKU-ABC-001\",\n      \"is_manually_assigned_lot\": true,\n      \"lot_date\": \"2024-06-01\",\n      \"lot_number\": \"LOT-2024-001\"\n    },\n    {\n      \"quantity\": 1,\n      \"sku\": \"SKU-XYZ-002\",\n      \"is_manually_assigned_lot\": true,\n      \"lot_date\": \"2024-11-01\",\n      \"lot_number\": \"LOT-2024-003\"\n    }\n  ]\n}")
  .asString();
```

```php default
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.shipbob.com/2026-01/shipment/1:updateLineItemsBySku', [
  'body' => '{
  "items": [
    {
      "quantity": 5,
      "sku": "SKU-ABC-001",
      "is_manually_assigned_lot": true,
      "lot_date": "2024-06-01",
      "lot_number": "LOT-2024-001"
    },
    {
      "quantity": 1,
      "sku": "SKU-XYZ-002",
      "is_manually_assigned_lot": true,
      "lot_date": "2024-11-01",
      "lot_number": "LOT-2024-003"
    }
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp default
using RestSharp;

var client = new RestClient("https://api.shipbob.com/2026-01/shipment/1:updateLineItemsBySku");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"items\": [\n    {\n      \"quantity\": 5,\n      \"sku\": \"SKU-ABC-001\",\n      \"is_manually_assigned_lot\": true,\n      \"lot_date\": \"2024-06-01\",\n      \"lot_number\": \"LOT-2024-001\"\n    },\n    {\n      \"quantity\": 1,\n      \"sku\": \"SKU-XYZ-002\",\n      \"is_manually_assigned_lot\": true,\n      \"lot_date\": \"2024-11-01\",\n      \"lot_number\": \"LOT-2024-003\"\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift default
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["items": [
    [
      "quantity": 5,
      "sku": "SKU-ABC-001",
      "is_manually_assigned_lot": true,
      "lot_date": "2024-06-01",
      "lot_number": "LOT-2024-001"
    ],
    [
      "quantity": 1,
      "sku": "SKU-XYZ-002",
      "is_manually_assigned_lot": true,
      "lot_date": "2024-11-01",
      "lot_number": "LOT-2024-003"
    ]
  ]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.shipbob.com/2026-01/shipment/1:updateLineItemsBySku")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```