# Manage & Track Freight Shipments

Once freight is booked, these endpoints retrieve it, download its paperwork, follow it in transit, and cancel it if plans change. See [Quote & Book Freight](/apis/shipengine/docs/freight/freight-quote-book) for the booking itself.

## List Freight Shipments

**GET /v1/freight/shipments**

Return the freight shipments on your account, most recently created first.

Results are paginated with `page` and `page_size`. The default page size is 25 and the maximum is 500 — larger values are capped rather than rejected, and `page` values below 1 are treated as 1.


```json
{
  "shipments": [
    {
      "freight_shipment_id": "se-98765432",
      "freight_provider_account_id": "se-4821",
      "freight_provider_name": "UNISHIPPERS",
      "status": "in_transit",
      "bol_number": "BOL-20260417-4821",
      "pro_number": "072-51293847",
      "documents": [
        {
          "type": "bill_of_lading",
          "url": "/v1/downloads/10/a1b2c3d4e5f6/bill_of_lading.pdf"
        }
      ]
    }
  ],
  "total": 137,
  "page": 1,
  "page_size": 25,
  "pages": 6
}
```

Freight shipments cannot be batched
Freight shipments are not eligible for the [batch](/apis/shipengine/docs/labels/bulk) endpoints. Including one in a batch returns a `400` with error code `freight_shipment_not_batchable`, and the message lists the offending shipment IDs. Print freight paperwork from [List freight shipment documents](#list-freight-shipment-documents) instead — the Bill of Lading and pallet labels are generated at booking, not by a batch process.

## Get Freight Shipment by ID

**GET /v1/freight/shipments/:freight_shipment_id**

Retrieve a single freight shipment with its current status, carrier reference numbers, and documents.

The `status` here is the stored status, updated as tracking is received. To pull fresh status from the carrier, use [Get freight shipment tracking](#get-freight-shipment-tracking).

An unknown ID returns a `404` with error code `invalid_identifier`, and `field_name` set to `freight_shipment_id`.

Unknown IDs are reported two different ways
This endpoint, [List freight shipment documents](#list-freight-shipment-documents), and [Cancel a freight shipment](#cancel-a-freight-shipment) report an unknown `freight_shipment_id` as `invalid_identifier`. [Get freight shipment tracking](#get-freight-shipment-tracking) reports the same condition as `freight_shipment_not_found`. Both are `404`s; handle both if you branch on `error_code`.

## List Freight Shipment Documents

**GET /v1/freight/shipments/:freight_shipment_id/documents**

List the paperwork for a shipment, with a download URL for each document.


```json
{
  "documents": [
    {
      "type": "bill_of_lading",
      "created_at": "2026-04-16T14:32:07Z",
      "url": "/v1/downloads/10/a1b2c3d4e5f6/bill_of_lading.pdf"
    },
    {
      "type": "pallet_label",
      "created_at": "2026-04-16T14:32:07Z",
      "url": "/v1/downloads/10/a1b2c3d4e5f6/pallet_label.pdf"
    }
  ]
}
```

Each `url` is a path relative to the API host — resolve it against `https://api.shipengine.com` and fetch it with your API key to get the PDF.

Documents appear as the shipment progresses. The Bill of Lading and pallet labels are generated at booking; a weight and inspection certificate appears if the carrier reweighs the freight; the proof of delivery only exists after delivery. Poll this endpoint, or watch tracking events, rather than expecting the full set up front.

TIP
Print the Bill of Lading before the driver arrives. LTL drivers will not collect freight without one, and pallet labels need to be affixed to each handling unit so the freight can be identified at every terminal it passes through.

### Document Types

| Type | Description |
|  --- | --- |
| `bill_of_lading` | The BOL — the contract of carriage and the document the driver signs at pickup. |
| `pallet_label`, `pallet_label_4x6` | Labels to affix to each handling unit. |
| `bol_pallet_label_combined` | The BOL and pallet labels as a single PDF. |
| `proof_of_delivery` | The signed delivery receipt, available after delivery. |
| `weight_and_inspection` | The carrier's reweigh or reclassification certificate. Its arrival usually means a billing adjustment is coming. |
| `quote` | A printable copy of the quote. |
| `packing_list` | The packing list for the shipment. |
| `certificate_of_insurance`, `certificate_of_insurance_instructions` | Cargo insurance paperwork, when insurance was purchased. |
| `certificate_of_origin_us`, `certificate_of_origin_usmca`, `certificate_of_origin_usmca_instructions` | Origin certificates for cross-border freight. |
| `international_commercial_invoice` | The commercial invoice for cross-border freight. |


## Get Freight Shipment Tracking

**GET /v1/freight/shipments/:freight_shipment_id/tracking**

Retrieve live status and event history from the carrier for a booked shipment.

The shipment is tracked by its PRO number when the carrier has assigned one, and by its BOL number otherwise.


```json
{
  "freight_shipment_id": "se-98765432",
  "freight_provider_account_id": "se-4821",
  "freight_provider_name": "UNISHIPPERS",
  "carrier_scac": "FXFE",
  "bol_number": "BOL-20260417-4821",
  "pro_number": "072-51293847",
  "status": "in_transit",
  "tracking_status": "in_transit",
  "estimated_delivery_date": "2026-04-22",
  "tracking_url": "https://www.fedex.com/fedextrack/?trknbr=07251293847",
  "last_event_at": "2026-04-20T14:05:00Z",
  "events": [
    {
      "occurred_at": "2026-04-17T10:22:00Z",
      "status": "at_pickup",
      "carrier_code": "AP",
      "description": "Freight picked up in Grand Rapids, MI",
      "documents": []
    },
    {
      "occurred_at": "2026-04-20T14:05:00Z",
      "status": "in_transit",
      "carrier_code": "AF",
      "description": "Departed terminal in Grand Rapids, MI",
      "documents": []
    }
  ]
}
```

Read `status` for a coarse state and `tracking_status` for the carrier's detail. Both are described in [Statuses](/apis/shipengine/docs/freight/freight-overview#statuses). `estimated_delivery_date` is a `YYYY-MM-DD` date, while `occurred_at` and `last_event_at` are timestamps; carriers that report only a date return midnight as the time.

Events can carry documents, most usefully a proof of delivery attached to the delivery event.

### Before a PRO Number Exists

A shipment with neither a PRO nor a BOL number cannot be tracked, and returns:


```json
{
  "request_id": "4b8e2f4c-1f6f-4b1a-9c4a-8f5e6d7c8b9a",
  "errors": [
    {
      "error_source": "shipengine",
      "error_type": "business_rules",
      "error_code": "freight_tracking_not_available",
      "message": "Tracking is not yet available for this shipment. A PRO or BOL number is required."
    }
  ]
}
```

This is expected in the window right after booking. Treat it as "not yet" rather than as a failure, and retry later.

The other outcomes this endpoint can return:

| Status / `error_code` | Meaning |
|  --- | --- |
| `404` `freight_shipment_not_found` | No shipment with that `freight_shipment_id` exists on your account. |
| `400` `freight_tracking_not_available` | The shipment exists but has neither a PRO nor a BOL number yet. Retry later. |
| `400` `freight_connection_inactive` | The freight provider connection the shipment was booked through is no longer active, so the carrier cannot be queried. The stored shipment record is still readable with [Get freight shipment by ID](#get-freight-shipment-by-id). |
| `404` `freight_tracking_not_found` | The carrier has no information for the number yet — also common before the freight is first scanned. |


A successful call also writes the carrier's status back to the stored shipment — its `status`, tracking status, and last-event timestamp — so [Get freight shipment by ID](#get-freight-shipment-by-id) reflects what tracking last returned. The write is best-effort: if it fails, you still get the tracking response. The PRO number is not updated this way, so a shipment tracked by BOL number keeps a `null` `pro_number` in the stored record.

## Get Freight Tracking by Number

**GET /v1/freight/tracking**

Track any freight shipment by its PRO or BOL number, whether or not it was booked through ShipStation API. This is the endpoint to use for freight booked elsewhere, or when all you have is a number from a supplier.


```http
GET /v1/freight/tracking?freight_provider_account_id=se-4821&tracking_number=072-51293847&tracking_type=pro_number&carrier_scac=FXFE
```

| Parameter | Required | Description |
|  --- | --- | --- |
| `freight_provider_account_id` | Yes | The freight provider account to track through, from [List freight provider accounts](/apis/shipengine/docs/freight/freight-quote-book#list-freight-provider-accounts). Omitting it, or sending a value that is not a valid account ID, returns a `400` with error code `freight_provider_id_required`. |
| `tracking_number` | Yes | The PRO or BOL number. |
| `tracking_type` | Yes | `pro_number` or `bol_number`. |
| `carrier_scac` | Yes | The carrier's SCAC. The freight provider needs it to resolve a tracking number, so the request is rejected without it. |


The response is the same shape as shipment tracking, minus `freight_shipment_id`, `status`, and `estimated_delivery_date`, since the number may not correspond to a shipment on your account. Read `tracking_status` for the carrier's state, and take the estimated delivery date from the events if you need it.

Nothing is stored as a result of this call — it is a pass-through to the provider. Tracking a number that happens to belong to one of your own shipments does not update that shipment's stored status; use [Get freight shipment tracking](#get-freight-shipment-tracking) for that.

`carrier_scac` in the response is the SCAC the carrier reported back, not the one you sent, so it can be `null` even on a successful lookup.

Its failure modes:

| Status / `error_code` | Meaning |
|  --- | --- |
| `400` `freight_provider_id_required` | `freight_provider_account_id` was omitted, or is not a valid account ID. The message names the parameter `seller_freight_provider_id`, which is what it used to be called. |
| `400` field validation (`field_value_required` or `unspecified`) | `tracking_number`, `tracking_type`, or `carrier_scac` was omitted, or `tracking_type` was something other than `pro_number` or `bol_number`. `field_name` names the parameter. |
| `400` `freight_connection_inactive` | The `freight_provider_account_id` is not an active connection on your account. |
| `404` `freight_tracking_not_found` | The provider returned nothing for that number. Either the carrier has not scanned the freight yet, or the number and SCAC do not match a shipment the provider can see. |


## Cancel a Freight Shipment

**DELETE /v1/freight/shipments/:freight_shipment_id**

Cancel a booked shipment with the carrier.


```json
{
  "freight_shipment_id": "se-98765432",
  "cancelled_transactions": [
    "7f1c9a52-3d84-4f0e-9b3a-2c6d5e4f8a10",
    "3ac81f60-92b7-4de1-8f04-5b9a7c2e6d13"
  ]
}
```

`cancelled_transactions` lists what was actually cancelled at the provider — typically the booked shipment and the pickup request.

Cancellation windows are set by the carrier, not by ShipStation API. Freight that has already been picked up generally cannot be cancelled, and the carrier's rejection comes back as a `400 Bad Request` whose `message` is the provider's wording behind the fixed prefix `A shipping carrier error occurred: ` — see [Errors](/apis/shipengine/docs/freight/freight-quote-book#errors) for the full shape. Cancel as early as you can, and treat a `400` here as "the carrier said no" rather than as a client error.