Truckload Unified Arrangements [Beta]
Beta: available on request. Truckload Unified Arrangements is a beta feature; documentation and behavior are subject to change before general availability. Contact your project44 account team to enable it for your tenant.
Overview
Full Truckload (FTL) is the one visibility mode that has historically created shipments through its own endpoint, POST /api/v4/tl/shipments, rather than the unified shipment API used by ocean, rail, and the other modes. Truckload Unified Arrangements brings FTL onto the same POST /api/v4/shipments/tracking endpoint and the same structured plan, so you can create truckload shipments with an explicit declaration of the carrier and the route.
Every truckload shipment you create through this API is built from three parts:
- Transportation mode identifies the shipment as truckload. You declare it with
plan.expectedTransportationModesset to["TRUCKLOAD"]. This is what activates truckload tracking and validation; truckload does not use scope tags. - A carrier arrangement (
TRUCKLOAD_CARRIER) declares the trucking company that moves the load and supplies the identifiers the platform needs to activate tracking, including the equipment (phone, vehicle, or device) used to locate the truck. - A route declares the planned stops, their appointment windows, and their locations. Truckload requires at least an origin and a destination, so the route is a first-class part of the plan rather than something the platform discovers.
plan.expectedTransportationModes, plan.arrangements, and plan.route extend the existing plan object in the unified shipment schema alongside top-level fields such as id and identifiers.
This page is for developers and logistics professionals who integrate with the project44 API to track truckload shipments. If you're migrating an existing FTL integration, each business scenario pairs the legacy POST /api/v4/tl/shipments payload with its unified-arrangements equivalent.
Before you begin
Before using the Truckload Unified Arrangements feature, make sure you have the following:
- An active project44 API client ID and secret, used to generate bearer tokens for authentication
- The correct permissions enabled on your tenant for the features you intend to use; contact your project44 account team if you're unsure
- Familiarity with the
POST /api/v4/shipments/trackingendpoint and the unified shipment schema - The information required to track a truckload shipment: a carrier identifier, at least two stops with appointment windows and locations, and, in most cases, an equipment identifier
- Access to the appropriate regional endpoint for your environment:
- North America:
https://na12.api.project44.com/api/v4/shipments/tracking - Europe:
https://eu12.api.project44.com/api/v4/shipments/tracking - Sandbox:
https://na12.api.sandbox.p-44.com/api/v4/shipments/tracking
- North America:
The plan
Every truckload shipment you create includes a plan object at the top level of the unified shipment structure. For truckload you configure three fields:
expectedTransportationModes: set to["TRUCKLOAD"]to declare the mode. This drives truckload validation and sets the mode on the route segments the platform builds.arrangements: an array of objects. For truckload, the arrangement that matters is theTRUCKLOAD_CARRIERarrangement, which identifies the trucking company and provides the references needed to track the load.route: the planned stops, in order, with their appointment windows and locations.
The following shows how these fields fit within the broader shipment structure:
{ "identifiers": [ { "type": "BILL_OF_LADING", "value": "BOL-XPOL-2026-0455" }, { "type": "CARRIER_SCAC", "value": "XPOL" } ], "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "scac": "XPOL", "billOfLadingNumber": "BOL-XPOL-2026-0455", "equipmentIdentifiers": [ { "identifier": { "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }, "priority": 1 } ] } } } ], "route": { "stops": [ { "sequenceNumber": 1, "type": "PICKUP", "location": { "name": "Lakeside Manufacturing", "address": { "addressLines": ["2200 W Canal St"], "city": "Milwaukee", "state": "WI", "postalCode": "53233", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-03T08:00:00-05:00", "endDateTime": "2026-08-03T12:00:00-05:00" } }, { "sequenceNumber": 2, "type": "DELIVERY", "location": { "name": "Riverside Distribution Center", "address": { "addressLines": ["1400 S Kostner Ave"], "city": "Chicago", "state": "IL", "postalCode": "60623", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-03T15:00:00-05:00", "endDateTime": "2026-08-03T18:00:00-05:00" } } ] } } }
Example dates are illustrative. The payloads on this page use fixed 2026 appointment dates for readability. Because truckload requires at least one appointment window in the future, replace the example dates with upcoming dates before you submit a request.
Transportation mode
Set plan.expectedTransportationModes to ["TRUCKLOAD"]. This is how the platform recognizes the shipment as truckload: it runs the truckload validation rules and sets the mode on the route segments generated from plan.route. Truckload does not use plan.scope; scope tags are specific to other modes and are ignored for truckload.
Arrangements
The arrangements field is an array of objects. Each arrangement declares one party involved in the shipment. For truckload, the relevant arrangement is TRUCKLOAD_CARRIER, which identifies the trucking company that moves the load. See The carrier arrangement for the field-level reference.
Route
The route field declares the shipment's planned path. It contains a stops array; each stop carries a sequenceNumber, a type, a location, and an arrivalWindow (and optionally a departureWindow). The platform orders stops by sequenceNumber, mints a stop ID for each, and builds the route segments between consecutive stops. See The planned route for the field-level reference and the validation rules truckload enforces.
Declare the route on plan.route. Do not also send a top-level routeInfo object: plan.route is the customer-declared path, and the persisted routeInfo is derived from it. Supplying both is rejected with a validation error.
The carrier arrangement
The TRUCKLOAD_CARRIER arrangement represents the trucking company that moves the load. A truckload shipment must resolve to at least one carrier identifier, so every shipment includes a TRUCKLOAD_CARRIER arrangement with a scac, dotNumber, or mcNumber.
Each arrangement supports two top-level identifier fields used to match it on subsequent updates:
| Name | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | No | Platform-generated UUID assigned when the arrangement is first created. Returned in the response. Provide this on subsequent requests to update the same arrangement. |
referenceId | string | No | Customer-provided string. Tenant-scoped and immutable after creation: it cannot be changed, cleared, or added once the arrangement has been stored. Use this as a stable handle when you want to match arrangements by your own identifier rather than the platform-generated id. |
See Arrangement behavior on POST and PUT for how id and referenceId are used during matching.
The carrier details live under details.truckloadCarrier.
Fields
| Name | Type | Required | Description |
|---|---|---|---|
scac | string | Conditional | Standard carrier alpha code. At least one of scac, dotNumber, or mcNumber is required so the platform can resolve the carrier. |
dotNumber | string | Conditional | US DOT number. Satisfies the carrier-identifier requirement on its own. |
mcNumber | string | Conditional | Motor carrier (MC) number. Satisfies the carrier-identifier requirement on its own. |
nmcCarrierId | string | No | National motor carrier ID used by some connections. |
masterCarrierId | string | No | Master carrier ID used to group carrier entities. |
billOfLadingNumber | string | No | Bill of lading number for the shipment. |
orderNumber | string | No | Order number for the shipment. |
equipmentIdentifiers | array | No | Tracking methods for the load. See Equipment identifiers. |
stops | array | No | Carrier-specific stops for this leg. For a single-carrier truckload, declare the route once on plan.route instead. |
The carrier fields are promoted to shipment-level identifiers when the arrangement is stored: scac becomes CARRIER_SCAC, dotNumber becomes CARRIER_US_DOT_NUMBER, mcNumber becomes CARRIER_MC_NUMBER, billOfLadingNumber becomes BILL_OF_LADING, and orderNumber becomes ORDER. If you also list any of these at the top level of the request, the values must match the arrangement; a conflict is rejected with ARRANGEMENT_IDENTIFIER_MISMATCH.
Equipment identifiers
Equipment identifiers tell the platform how to track the vehicle carrying the load. Each entry pairs an identifier (a type and a value) with an optional priority. Lower priority numbers are tried first, so you can list a preferred tracking method and one or more fallbacks.
| Name | Type | Required | Description |
|---|---|---|---|
identifier.type | string | Yes | Tracking method. See the accepted values below. |
identifier.value | string | Yes | The value for the tracking method (phone number, vehicle ID, plate, or device ID). |
priority | integer | No | Order in which tracking methods are attempted. Lower is tried first. |
Accepted identifier.type values:
| Value | Description |
|---|---|
MOBILE_PHONE_NUMBER | Driver's mobile phone number, tracked through the carrier's mobile app or an SMS-based method. |
VEHICLE_ID | Vehicle or truck ID reported by the carrier's ELD / telematics connection. |
LICENSE_PLATE | Tractor or trailer license plate. |
SENSITECH_DEVICE_ID | Sensitech monitoring device ID (reefer / condition monitoring). |
EMERSON_DEVICE_ID | Emerson monitoring device ID (reefer / condition monitoring). |
TIVE_DEVICE_ID | Tive tracker device ID. |
Equipment identifiers are also promoted to shipment-level identifiers of the matching type.
The planned route
Truckload shipments must declare their route at creation time. The route lives on plan.route.stops as an ordered list of stops. The platform sorts stops by sequenceNumber, assigns each a stop ID, preserves the referenceId and type you supply, and generates the route segments between consecutive stops using the mode from expectedTransportationModes.
Stop fields
| Name | Type | Required | Description |
|---|---|---|---|
sequenceNumber | integer | Yes | Position of the stop in the route, starting at 1 for the origin. Stops are ordered by this value. |
type | string | Yes | Stop type. See the accepted values below. |
referenceId | string | No | Your own identifier for the stop. Preserved on the derived stop and used to bind incoming events to the right stop. |
location | object | Yes | Where the stop is. See Stop location fields. |
arrivalWindow | object | Yes | Appointment window for arrival. startDateTime and endDateTime, both offset date-times. An endDateTime is required. |
departureWindow | object | No | Appointment window for departure, if tracked separately. |
Accepted type values for truckload:
| Value | Description |
|---|---|
PICKUP | Where the vehicle is loaded with the shipment. |
ORIGIN | Where the truck is filled by the shipper with the goods to be transported. |
TRANSFER | An intermediate transfer stop. |
DELIVERY | Where the shipment is delivered. |
DESTINATION | Where the shipment is finally delivered. |
Stop location fields
| Name | Type | Required | Description |
|---|---|---|---|
name | string | No | Human-readable name for the location. |
address | object | Conditional | Postal address. See below. Provide an address or coordinates. |
address.addressLines | array of strings | No | Street address lines. |
address.city | string | Conditional | City. Required when the location is given by address. |
address.state | string | Conditional | State or province. Required when the location is given by address. |
address.postalCode | string | No | Postal / ZIP code. |
address.country | string | Conditional | ISO 3166-1 alpha-2 country code. Required when the location is given by address. |
coordinates.latitude | number | Conditional | Latitude. Provide coordinates or an address. |
coordinates.longitude | number | Conditional | Longitude. Provide coordinates or an address. |
timeZone | string | No | IANA time zone for the stop (for example, America/Chicago). |
Route validation rules
Truckload applies the following rules at create time. Each failure returns a machine-readable error code in the response.
| Rule | Error code |
|---|---|
| At least two stops are required. | TRUCKLOAD_MISSING_SHIPMENT_STOP |
| Every stop must have an arrival appointment window. | TRUCKLOAD_MISSING_APPOINTMENT_WINDOW |
Each arrival window must include an endDateTime. | TRUCKLOAD_APPOINTMENT_WINDOW_NO_END_DATE |
| At least one appointment must be in the future. | TRUCKLOAD_APPOINTMENT_WINDOW_IN_THE_PAST |
| Every stop must carry enough location detail: valid coordinates, or an address with at least city, state, and country. | TRUCKLOAD_INSUFFICIENT_LOCATION_INFORMATION |
A carrier identifier (scac, dotNumber, or mcNumber) must be resolvable. | TRUCKLOAD_MISSING_CARRIER_IDENTIFIER |
The time zone that used to sit inside appointmentWindow.localTimeZoneIdentifier in the legacy API now lives on the stop's location.timeZone. Appointment windows carry offset date-times directly.
Requirements
To create a valid truckload shipment through this API:
- Set
plan.expectedTransportationModesto["TRUCKLOAD"]. - Include a
TRUCKLOAD_CARRIERarrangement with a resolvable carrier identifier (scac,dotNumber, ormcNumber). - Provide at least two stops on
plan.route.stops, each with a location and an arrival appointment window with an end date. At least one appointment must be in the future. - Add an equipment identifier so tracking can start immediately. Depending on the carrier connection type it may not be required, but sending one is the reliable default.
Business scenarios
Truckload tracking spans a range of setups: loads booked directly with an asset carrier, loads tracked by different equipment methods, and multi-stop routes. Previously, every setup used the same payload shape on POST /api/v4/tl/shipments: a single carrierIdentifier, a flat shipmentIdentifiers list, a shipmentStops array, and a top-level equipmentIdentifiers list. That model spread the carrier, its shipment references, and its equipment across separate top-level fields with no clear ownership.
The TRUCKLOAD_CARRIER arrangement and plan.route consolidate those signals and move truckload onto the unified endpoint. Each of the following scenarios pairs the legacy payload with its unified-arrangements equivalent to support a one-by-one migration of an existing integration.
Same validation, earlier. The unified payloads enforce the same truckload rules the legacy endpoint applies (two stops, appointment windows, a resolvable carrier), but they run at create time against a single structured request, so you get actionable errors before tracking is attempted.
Scenario 1: FTL booked directly with an asset carrier
Business case: A shipper tenders a full truckload directly to an asset carrier and tracks the driver by mobile phone.
- Transportation mode:
TRUCKLOAD - Key arrangement:
TRUCKLOAD_CARRIER(SCAC + bill of lading + mobile phone)
| Before this API | With unified arrangements |
|---|---|
{ "carrierIdentifier": { "type": "SCAC", "value": "XPOL" }, "shipmentIdentifiers": [{ "type": "BILL_OF_LADING", "value": "BOL-XPOL-2026-0455" }], "shipmentStops": [ { "stopNumber": 1, "appointmentWindow": { "startDateTime": "2026-08-03T08:00:00", "endDateTime": "2026-08-03T12:00:00", "localTimeZoneIdentifier": "America/Chicago" }, "location": { "address": { "addressLines": ["2200 W Canal St"], "city": "Milwaukee", "state": "WI", "postalCode": "53233", "country": "US" } } }, { "stopNumber": 2, "appointmentWindow": { "startDateTime": "2026-08-03T15:00:00", "endDateTime": "2026-08-03T18:00:00", "localTimeZoneIdentifier": "America/Chicago" }, "location": { "address": { "addressLines": ["1400 S Kostner Ave"], "city": "Chicago", "state": "IL", "postalCode": "60623", "country": "US" } } } ], "equipmentIdentifiers": [{ "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }] } | { "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "scac": "XPOL", "billOfLadingNumber": "BOL-XPOL-2026-0455", "equipmentIdentifiers": [ { "identifier": { "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }, "priority": 1 } ] } } } ], "route": { "stops": [ { "sequenceNumber": 1, "type": "PICKUP", "location": { "address": { "addressLines": ["2200 W Canal St"], "city": "Milwaukee", "state": "WI", "postalCode": "53233", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-03T08:00:00-05:00", "endDateTime": "2026-08-03T12:00:00-05:00" } }, { "sequenceNumber": 2, "type": "DELIVERY", "location": { "address": { "addressLines": ["1400 S Kostner Ave"], "city": "Chicago", "state": "IL", "postalCode": "60623", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-03T15:00:00-05:00", "endDateTime": "2026-08-03T18:00:00-05:00" } } ] } } } |
What changed: The carrier, its bill of lading, and the driver's phone number all sit inside one TRUCKLOAD_CARRIER arrangement instead of three separate top-level fields. The stop time zone moves from appointmentWindow.localTimeZoneIdentifier onto location.timeZone, and appointment windows carry offset date-times.
Scenario 2: FTL tracked by ELD vehicle ID
Business case: The carrier tracks through an ELD / telematics connection rather than a driver's phone, so the load is anchored on a vehicle ID.
- Transportation mode:
TRUCKLOAD - Key arrangement:
TRUCKLOAD_CARRIERwith aVEHICLE_IDequipment identifier
| Before this API | With unified arrangements |
|---|---|
{ "carrierIdentifier": { "type": "SCAC", "value": "KNXT" }, "shipmentIdentifiers": [{ "type": "ORDER", "value": "PO-88213" }], "shipmentStops": [ /* origin and destination stops */ ], "equipmentIdentifiers": [{ "type": "VEHICLE_ID", "value": "TRK-4471" }] } | { "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "scac": "KNXT", "orderNumber": "PO-88213", "equipmentIdentifiers": [ { "identifier": { "type": "VEHICLE_ID", "value": "TRK-4471" }, "priority": 1 } ] } } } ], "route": { "stops": [ /* origin and destination stops */ ] } } } |
What changed: The vehicle ID is scoped to the carrier arrangement that owns it, and the order number is declared explicitly (orderNumber) rather than inferred from a flat identifier list.
Scenario 3: FTL with a carrier identified by DOT or MC number
Business case: The carrier does not have a SCAC on file, or the integration keys off the DOT / MC number instead.
- Transportation mode:
TRUCKLOAD - Key arrangement:
TRUCKLOAD_CARRIERwithdotNumber(and optionallymcNumber)
| Before this API | With unified arrangements |
|---|---|
{ "carrierIdentifier": { "type": "DOT_NUMBER", "value": "2131234" }, "shipmentIdentifiers": [{ "type": "BILL_OF_LADING", "value": "BOL-2026-0512" }], "shipmentStops": [ /* origin and destination stops */ ], "equipmentIdentifiers": [{ "type": "LICENSE_PLATE", "value": "IL-AB12345" }] } | { "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "dotNumber": "2131234", "mcNumber": "MC-654321", "billOfLadingNumber": "BOL-2026-0512", "equipmentIdentifiers": [ { "identifier": { "type": "LICENSE_PLATE", "value": "IL-AB12345" }, "priority": 1 } ] } } } ], "route": { "stops": [ /* origin and destination stops */ ] } } } |
What changed: The carrier can be resolved from any of scac, dotNumber, or mcNumber, each in its own field, instead of a single typed carrierIdentifier. Supply more than one when you have them.
Scenario 4: Multi-stop FTL
Business case: A single truckload picks up at one origin and delivers to two consignees, with an intermediate stop in between.
- Transportation mode:
TRUCKLOAD - Key arrangement:
TRUCKLOAD_CARRIER+ a three-stop route
| Before this API | With unified arrangements |
|---|---|
{ "carrierIdentifier": { "type": "SCAC", "value": "XPOL" }, "shipmentIdentifiers": [{ "type": "BILL_OF_LADING", "value": "BOL-XPOL-2026-0460" }], "shipmentStops": [ { "stopNumber": 1, "appointmentWindow": { "startDateTime": "2026-08-05T07:00:00", "endDateTime": "2026-08-05T10:00:00", "localTimeZoneIdentifier": "America/Chicago" }, "location": { "address": { "addressLines": ["2200 W Canal St"], "city": "Milwaukee", "state": "WI", "postalCode": "53233", "country": "US" } } }, { "stopNumber": 2, "appointmentWindow": { "startDateTime": "2026-08-05T13:00:00", "endDateTime": "2026-08-05T15:00:00", "localTimeZoneIdentifier": "America/Chicago" }, "location": { "address": { "addressLines": ["500 S 27th St"], "city": "Milwaukee", "state": "WI", "postalCode": "53215", "country": "US" } } }, { "stopNumber": 3, "appointmentWindow": { "startDateTime": "2026-08-05T18:00:00", "endDateTime": "2026-08-05T21:00:00", "localTimeZoneIdentifier": "America/Chicago" }, "location": { "address": { "addressLines": ["1400 S Kostner Ave"], "city": "Chicago", "state": "IL", "postalCode": "60623", "country": "US" } } } ], "equipmentIdentifiers": [{ "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }] } | { "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "scac": "XPOL", "billOfLadingNumber": "BOL-XPOL-2026-0460", "equipmentIdentifiers": [ { "identifier": { "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }, "priority": 1 } ] } } } ], "route": { "stops": [ { "sequenceNumber": 1, "type": "PICKUP", "referenceId": "PICKUP-MKE", "location": { "address": { "addressLines": ["2200 W Canal St"], "city": "Milwaukee", "state": "WI", "postalCode": "53233", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-05T07:00:00-05:00", "endDateTime": "2026-08-05T10:00:00-05:00" } }, { "sequenceNumber": 2, "type": "DELIVERY", "referenceId": "DROP-MKE-SOUTH", "location": { "address": { "addressLines": ["500 S 27th St"], "city": "Milwaukee", "state": "WI", "postalCode": "53215", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-05T13:00:00-05:00", "endDateTime": "2026-08-05T15:00:00-05:00" } }, { "sequenceNumber": 3, "type": "DELIVERY", "referenceId": "DROP-CHI", "location": { "address": { "addressLines": ["1400 S Kostner Ave"], "city": "Chicago", "state": "IL", "postalCode": "60623", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-05T18:00:00-05:00", "endDateTime": "2026-08-05T21:00:00-05:00" } } ] } } } |
What changed: Stop order is declared by sequenceNumber, and each stop carries a referenceId you control. The platform builds a route segment between each consecutive pair of stops, and inbound events that reference a stop by its referenceId bind to the right stop and segment automatically.
Scenario 5: FTL with prioritized fallback tracking
Business case: The carrier can be tracked by more than one method and the integration wants a preferred method with a fallback.
- Transportation mode:
TRUCKLOAD - Key arrangement:
TRUCKLOAD_CARRIERwith multiple prioritized equipment identifiers
| Before this API | With unified arrangements |
|---|---|
{ "carrierIdentifier": { "type": "SCAC", "value": "XPOL" }, "shipmentIdentifiers": [{ "type": "BILL_OF_LADING", "value": "BOL-XPOL-2026-0491" }], "shipmentStops": [ /* origin and destination stops */ ], "equipmentIdentifiers": [ { "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }, { "type": "VEHICLE_ID", "value": "TRK-4471" } ] } The two equipment identifiers were an unordered list; there was no way to say which to try first. | { "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "scac": "XPOL", "billOfLadingNumber": "BOL-XPOL-2026-0491", "equipmentIdentifiers": [ { "identifier": { "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }, "priority": 1 }, { "identifier": { "type": "VEHICLE_ID", "value": "TRK-4471" }, "priority": 2 } ] } } } ], "route": { "stops": [ /* origin and destination stops */ ] } } } |
What changed: The priority field makes the fallback order explicit. The platform tries the lowest priority number first (the mobile phone) and falls back to the next (the vehicle ID) when the preferred method can't be established.
Linking shipments via shared arrangements
When several truckload shipments move under the same carrier booking, give each shipment's TRUCKLOAD_CARRIER arrangement the same top-level referenceId. The platform treats arrangements that share a referenceId as the same underlying entity, so the shared arrangement provides a navigable link across those shipments. Each shipment still tracks independently.
{ "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "type": "TRUCKLOAD_CARRIER", "referenceId": "CARRIER-XPOL-BOOKING-77120", "details": { "truckloadCarrier": { "scac": "XPOL", "billOfLadingNumber": "BOL-XPOL-2026-0455" } } } ], "route": { "stops": [ /* origin and destination stops */ ] } } }
Because the two shipments' carrier arrangements share the same referenceId, the platform resolves them to one underlying entity. See Arrangement behavior on POST and PUT for the full matching rules around referenceId.
Endpoints
The following endpoints are available for truckload shipment tracking through the unified API.
POST /shipments/tracking
Creates a new shipment or appends data to an existing one. This operation is additive: it never removes existing arrangements. If you provide a shipment id that already exists, the operation merges the new data with the existing record.
Full path: POST /api/v4/shipments/tracking
Request headers
| Name | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | Bearer token. Format: Bearer <access-token> |
Content-Type | string | Yes | Must be application/json |
Request body parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | No | project44-assigned shipment UUID. Provide this to append data to an existing shipment. |
identifiers | array | No | Logistics identifiers associated with the shipment. Must not conflict with the carrier arrangement fields. |
plan.expectedTransportationModes | array of strings | Yes | Set to ["TRUCKLOAD"] to identify the shipment as truckload. |
plan.arrangements | array of objects | Yes | Declares the carrier. Include a TRUCKLOAD_CARRIER arrangement. See The carrier arrangement. |
plan.route.stops | array of objects | Yes | The planned stops, in order. At least two are required. See The planned route. |
attributes | array | No | User-defined custom attributes to associate with the shipment. |
entitledAccessGroups | array | No | Access groups entitled to visibility of this shipment. |
Do not send a top-level routeInfo object alongside plan.route; the persisted routeInfo is derived from the plan.
Request example
{ "identifiers": [ { "type": "BILL_OF_LADING", "value": "BOL-XPOL-2026-0455" }, { "type": "CARRIER_SCAC", "value": "XPOL" } ], "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "scac": "XPOL", "billOfLadingNumber": "BOL-XPOL-2026-0455", "equipmentIdentifiers": [ { "identifier": { "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }, "priority": 1 } ] } } } ], "route": { "stops": [ { "sequenceNumber": 1, "type": "PICKUP", "location": { "address": { "addressLines": ["2200 W Canal St"], "city": "Milwaukee", "state": "WI", "postalCode": "53233", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-03T08:00:00-05:00", "endDateTime": "2026-08-03T12:00:00-05:00" } }, { "sequenceNumber": 2, "type": "DELIVERY", "location": { "address": { "addressLines": ["1400 S Kostner Ave"], "city": "Chicago", "state": "IL", "postalCode": "60623", "country": "US" }, "timeZone": "America/Chicago" }, "arrivalWindow": { "startDateTime": "2026-08-03T15:00:00-05:00", "endDateTime": "2026-08-03T18:00:00-05:00" } } ] } } }
Response fields
| Name | Type | Description |
|---|---|---|
id | string | UUID of the created or updated shipment. |
identifiers | array | Logistics identifiers, including the ones promoted from the carrier arrangement. |
plan | object | The shipment plan, including expectedTransportationModes and arrangements (each with an assigned id). The planned route is returned as the derived routeInfo object below, not on plan. |
routeInfo | object | Stops and route segments derived from plan.route. |
shipmentShareLink | string | Publicly accessible link to the shipment details page. |
createdDateTime | string (date-time) | Timestamp when the shipment was created. Read-only. |
lastModifiedDateTime | string (date-time) | Timestamp when the shipment was last modified. Read-only. |
Response example
{ "id": "3b6bff66-c74b-475c-a222-ce6a92413650", "identifiers": [ { "type": "BILL_OF_LADING", "value": "BOL-XPOL-2026-0455" }, { "type": "CARRIER_SCAC", "value": "XPOL" }, { "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" } ], "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "id": "a1b2c3d4-5566-7788-99aa-bbccddeeff00", "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "scac": "XPOL", "billOfLadingNumber": "BOL-XPOL-2026-0455", "equipmentIdentifiers": [ { "identifier": { "type": "MOBILE_PHONE_NUMBER", "value": "+13125550147" }, "priority": 1 } ] } } } ] }, "routeInfo": { "stops": [ { "id": "c1000000-0000-4000-a000-000000000010", "type": "PICKUP" }, { "id": "c1000000-0000-4000-a000-000000000011", "type": "DELIVERY" } ], "routeSegments": [ { "id": "d1000000-0000-4000-a000-000000000010", "fromStopId": "c1000000-0000-4000-a000-000000000010", "toStopId": "c1000000-0000-4000-a000-000000000011", "transportationMode": "TRUCKLOAD" } ] }, "shipmentShareLink": "https://movement.project44.com/share/abc123xyz", "createdDateTime": "2026-07-23T09:15:00Z", "lastModifiedDateTime": "2026-07-23T09:15:00Z" }
Error codes
| Status | Description |
|---|---|
400 | Validation failed. The response body includes a machine-readable error code and an actionable message. See Truckload validation errors. |
401 | Authentication failed. Your bearer token is missing or invalid. |
403 | Your account doesn't have the required permission for this operation. |
404 | The shipment ID you provided doesn't exist. |
500 | An unexpected server error occurred. If this persists, contact project44 support. |
PUT /shipments/tracking
Updates an existing shipment. Unlike POST, the request payload defines the complete arrangement set for the shipment: arrangements absent from the request are unlinked, and details on matched arrangements are fully replaced rather than merged. See Arrangement behavior on POST and PUT for the precise rules.
Full path: PUT /api/v4/shipments/tracking
Request body parameters
The PUT request body uses the same schema as POST. You must provide id to identify the shipment you're replacing.
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | project44-assigned shipment UUID of the shipment to replace. |
plan.expectedTransportationModes | array of strings | Yes | Set to ["TRUCKLOAD"]. |
plan.arrangements | array of objects | Conditional | The arrangement set for the shipment. Omit the field or send null for a no-op; send [] to unlink every arrangement; send a non-empty list to snapshot-replace (any arrangement not included is then unlinked). See Arrangement behavior on POST and PUT. |
Request example
{ "id": "3b6bff66-c74b-475c-a222-ce6a92413650", "plan": { "expectedTransportationModes": ["TRUCKLOAD"], "arrangements": [ { "id": "a1b2c3d4-5566-7788-99aa-bbccddeeff00", "type": "TRUCKLOAD_CARRIER", "details": { "truckloadCarrier": { "scac": "XPOL", "billOfLadingNumber": "BOL-XPOL-2026-0455", "equipmentIdentifiers": [ { "identifier": { "type": "VEHICLE_ID", "value": "TRK-4471" }, "priority": 1 } ] } } } ] } }
Error codes
| Status | Description |
|---|---|
400 | Validation failed. The response body includes a machine-readable error code and an actionable message. |
401 | Authentication failed. Your bearer token is missing or invalid. |
403 | Your account doesn't have the required permission for this operation. |
404 | The shipment ID you provided doesn't exist. |
500 | An unexpected server error occurred. If this persists, contact project44 support. |
Arrangement behavior on POST and PUT
Both methods match each request arrangement against stored arrangements by id or referenceId (see The carrier arrangement for how these are populated). The methods differ in what they write to a matched arrangement, and whether arrangements omitted from the payload are affected.
Arrangements are shared across shipments. An arrangement is a tenant-scoped entity that can be linked to many shipments at the same time. The same
id(orreferenceId) always refers to the same underlying arrangement, regardless of which shipment is being updated. Updating an arrangement via any one shipment mutates the underlying entity, so the new state is visible on every other shipment the arrangement is linked to.
POST: append-and-link with a partial merge
- A matched arrangement is updated by partial merge: only the fields present in the request are written. Fields absent from the request are left unchanged.
- An arrangement with no match is created and linked to the shipment.
- Arrangements omitted from the payload are left untouched. POST never unlinks.
PUT: snapshot-replace of the shipment's arrangement set
The shape of plan.arrangements in the request determines behavior:
plan.arrangements in the request | Behavior |
|---|---|
field absent or null | No-op. Arrangements and their links are untouched. |
empty list [] | Every arrangement linked to this shipment is unlinked. The arrangements themselves are not deleted. |
non-empty list [...] | Snapshot-replace (see the rules that follow). |
For a non-empty list:
- A matched arrangement has its
detailsfully replaced by the request'sdetails(subject to the immutability rules). Other top-level fields (type,referenceId) cannot change. - An arrangement with no match is created and linked.
- Stored arrangements linked to the shipment but absent from the request are unlinked. The arrangement entities are not deleted.
After a successful PUT with a non-empty plan.arrangements, a GET on the shipment returns exactly the arrangements supplied in the request, in the same order.
Immutability rules (POST and PUT)
The following are rejected with 400 Bad Request:
- Changing a stored arrangement's
referenceIdto a different non-null value. - Setting a
referenceIdon a stored arrangement whosereferenceIdisnull(it can only be set at creation). - Clearing a stored arrangement's
referenceId. - Two arrangements in the same payload sharing the same
referenceId. - Supplying both
idandreferenceIdwhere they resolve to different stored arrangements (identity conflict). - Supplying an
idthat does not exist.
Idempotency
Both methods are idempotent at the operation level when each arrangement carries a stable identifier. Retries that produce no state change emit no operations. Arrangements supplied with neither id nor referenceId are treated as new on every call: include a referenceId for retry-safe writes.
Truckload validation errors
Truckload requests are validated at create time. The following error codes are specific to truckload and returned with a 400 status.
| Error code | Meaning | How to fix |
|---|---|---|
TRUCKLOAD_MISSING_CARRIER_IDENTIFIER | No carrier identifier could be resolved. | Supply scac, dotNumber, or mcNumber on the TRUCKLOAD_CARRIER arrangement, or a matching top-level carrier identifier. |
TRUCKLOAD_MISSING_SHIPMENT_STOP | Fewer than two stops were provided. | Provide at least an origin and a destination in plan.route.stops. |
TRUCKLOAD_MISSING_APPOINTMENT_WINDOW | A stop has no arrival appointment window. | Add an arrivalWindow to every stop. The message names the stop number. |
TRUCKLOAD_APPOINTMENT_WINDOW_NO_END_DATE | A stop's arrival window has no endDateTime. | Add an endDateTime to every arrival window. |
TRUCKLOAD_APPOINTMENT_WINDOW_IN_THE_PAST | No appointment window is in the future. | Make sure at least one stop's appointment window is upcoming. |
TRUCKLOAD_INSUFFICIENT_LOCATION_INFORMATION | A stop lacks enough location detail. | Provide valid coordinates, or an address with at least city, state, and country. The message names the stop number. |
ARRANGEMENT_IDENTIFIER_MISMATCH | A carrier arrangement field conflicts with a top-level identifier of the same type. | Make the arrangement value and the top-level identifier match, or remove one of them. |
PLAN_ROUTE_AND_ROUTE_INFO_BOTH_PROVIDED | The request carried both plan.route and routeInfo. | Send the route on plan.route only; routeInfo is derived from it. |
For the shared error-response schema (400, 401, 403, 404) and the meaning of each field, see the Appendix.