Ocean Unified Arrangements [Beta]
Beta: available on request. Ocean 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
The Ocean Unified Arrangements feature lets you create and update ocean shipments using a structured plan that separates your intent from carrier-discovered data and realized execution. Every shipment you create through this API includes two components:
- Scope declares what kind of shipment you're tracking: for example, a full container load (FCL) or a roll-on/roll-off (RoRo) shipment.
- Arrangements declare which parties are involved and supply the identifiers the platform needs to activate tracking.
This model applies to all ocean shipment types: FCL, less-than-container load (LCL), and RoRo. The plan.scope and plan.arrangements fields extend the existing plan object in the unified shipment schema alongside top-level fields such as id, identifiers, and events.
This page is for developers and logistics professionals who integrate with the project44 API to track ocean shipments. Together, plan.scope and plan.arrangements:
- Declare the type of shipment and what tracking behavior to activate
- Identify the parties involved, such as the ocean carrier or freight forwarder
- Give the API the information it needs to validate your request upfront and return actionable error messages
Before you begin
Before using the Ocean 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. Freight-forwarder-managed shipments require an additional permission; contact your project44 account team if you're unsure
- Familiarity with the
POST /api/v4/shipments/trackingendpoint and the unified shipment schema - 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 ocean shipment you create includes a plan object at the top level of the unified shipment structure. The plan object contains two fields that you configure:
scope: an array of tags that declares what kind of shipment this isarrangements: an array of objects that identifies the parties involved and provides the references needed to track the shipment
The following shows how these fields fit within the broader shipment structure:
{ "id": "3b6bff66-c74b-475c-a222-ce6a92413650", "identifiers": [ { "type": "BILL_OF_LADING", "value": "BOL-KITDT6B6" }, { "type": "CARRIER_NAME", "value": "NTH INC" } ], "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "billOfLadingNumber": "BOL-MAEU-2026-001" } } } ] } }
Scope
The scope field is an array of tags. The first tag is the primary scope tag and determines the shipment type. You can add modifier tags after the primary tag to enable additional capabilities.
Primary scope tags:
| Tag | Shipment type |
|---|---|
OCEAN_FULL_CONTAINER_LOAD | Full Container Load (FCL) |
OCEAN_LESS_THAN_CONTAINER_LOAD | Less-than-Container Load (LCL) |
OCEAN_ROLL_ON_ROLL_OFF | Roll-on/roll-off (RoRo) wheeled cargo |
Modifier scope tags:
| Tag | Applies to | Description |
|---|---|---|
FREIGHT_FORWARDER | FCL, LCL, RoRo | Marks the shipment as freight-forwarder-managed. Requires a FREIGHT_FORWARDER arrangement and an additional product permission. |
OCEAN_SINGLE_CONTAINER | FCL only | Restricts tracking to a single container and suppresses sibling container discovery. Requires containerNumber on every OCEAN_CARRIER arrangement. |
Arrangements
The arrangements field is an array of objects. Each arrangement declares one party involved in the shipment. The following arrangement types are supported:
OCEAN_CARRIER: the ocean shipping lineFREIGHT_FORWARDER: the freight forwarder managing the shipmentSHIPPER: the cargo owner sending the goodsCONSIGNEE: the cargo owner receiving the goodsBILL_TO: the party responsible for freight chargesNOTIFY_PARTY: a party to be notified of shipment events
SHIPPER, CONSIGNEE, BILL_TO, and NOTIFY_PARTY are optional on any shipment and can carry contact details (company name, names, phone, email) and a full address. See Arrangement types for the field-level reference.
Arrangement types
Arrangements declare the parties involved in a shipment. Each arrangement has a type and a details object specific to that type.
Every arrangement, regardless of type, also supports two top-level identifier fields used to match the arrangement on subsequent updates:
| Name | Type | Required | Description |
|---|---|---|---|
id | string | 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.
OCEAN_CARRIER
Represents an ocean shipping line. The OCEAN_CARRIER arrangement is required for most ocean shipment types unless FREIGHT_FORWARDER is in scope. Multiple OCEAN_CARRIER arrangements can appear in a single plan, each with a different role.
Fields
| Name | Type | Required | Description |
|---|---|---|---|
scac | string | Yes | Standard carrier alpha code identifying the ocean carrier. |
roles | array of strings | No | Carrier roles. Accepted values: CONTRACTUAL, OPERATING, COLOADER. Defaults to ["CONTRACTUAL"] when omitted. |
billOfLadingNumber | string | No | Master bill of lading number issued by the carrier. |
bookingNumber | string | No | Booking number for the shipment. |
houseBillOfLadingNumber | string | No | House bill of lading number. |
containerNumber | string | Conditional | Required when OCEAN_SINGLE_CONTAINER is in scope. All OCEAN_CARRIER arrangements must use the same container number. |
serviceType | string | No | Extent of carrier service. See Service types. |
bookingType | string | No | Type of cargo booking. Inferred from the primary scope tag when omitted. See Booking types. |
Service types
The serviceType field declares where the carrier's responsibility begins and ends, that is, which legs of the journey (inland, port, ocean) the carrier's contract covers. When present, the value is used for demurrage and detention (D&D) calculations and to optimize milestone generation; it does not affect validation rules.
| Value | Origin | Destination | Description |
|---|---|---|---|
DOOR_TO_DOOR | Door (shipper's premises) | Door (consignee's premises) | Full door-to-door service. Carrier handles all legs including inland transport at both ends. |
DOOR_TO_RAIL_RAMP | Door (shipper's premises) | Rail ramp | Carrier picks up from shipper's door and delivers to a destination rail ramp. |
DOOR_TO_CONTAINER_YARD | Door (shipper's premises) | Container yard | Carrier picks up from shipper's door and delivers to a destination container yard. |
RAIL_RAMP_TO_DOOR | Rail ramp | Door (consignee's premises) | Carrier picks up from an origin rail ramp and delivers to consignee's door. |
RAIL_RAMP_TO_RAIL_RAMP | Rail ramp | Rail ramp | Carrier handles rail and ocean legs between ramps. |
RAIL_RAMP_TO_CONTAINER_YARD | Rail ramp | Container yard | Carrier picks up from an origin rail ramp and delivers to a destination container yard. |
CONTAINER_YARD_TO_DOOR | Container yard | Door (consignee's premises) | Carrier picks up from an origin container yard and delivers to consignee's door. |
CONTAINER_YARD_TO_RAIL_RAMP | Container yard | Rail ramp | Carrier picks up from an origin container yard and delivers to a destination rail ramp. |
CONTAINER_YARD_TO_CONTAINER_YARD | Container yard | Container yard | Carrier handles transport between yards including the ocean leg. |
CFS_TO_CFS | Container Freight Station (CFS) | Container Freight Station (CFS) | Typically used for LCL shipments where cargo is consolidated and deconsolidated at freight stations. |
CFS_TO_CONTAINER_YARD | Container Freight Station (CFS) | Container yard | Consolidation scenarios where cargo is consolidated at origin CFS. |
CONTAINER_YARD_TO_CFS | Container yard | Container Freight Station (CFS) | Deconsolidation scenarios where cargo is deconsolidated at destination CFS. |
Booking types
The bookingType field declares the type of ocean cargo booking the customer has with the carrier. This determines how the system treats containers, tracking, and consolidation behavior for the shipment.
| Value | Description |
|---|---|
FULL_CONTAINER_LOAD | The customer has booked an entire container. Used for OCEAN_FULL_CONTAINER_LOAD shipments and for OCEAN_LESS_THAN_CONTAINER_LOAD consolidation shipments (where the forwarder controls the entire container). |
LESS_THAN_CONTAINER_LOAD | The customer has booked space within a shared container. Multiple shippers share a single container, and the carrier or NVOCC manages consolidation. Used for standard OCEAN_LESS_THAN_CONTAINER_LOAD shipments. |
ROLL_ON_ROLL_OFF | The customer has booked wheeled cargo space on a RoRo vessel. Used for OCEAN_ROLL_ON_ROLL_OFF shipments. |
When bookingType is omitted, the system derives it from the primary scope tag:
| Primary scope tag | Default bookingType |
|---|---|
OCEAN_FULL_CONTAINER_LOAD | FULL_CONTAINER_LOAD |
OCEAN_LESS_THAN_CONTAINER_LOAD | LESS_THAN_CONTAINER_LOAD |
OCEAN_ROLL_ON_ROLL_OFF | ROLL_ON_ROLL_OFF |
For OCEAN_LESS_THAN_CONTAINER_LOAD consolidation shipments, the caller must explicitly set bookingType: "FULL_CONTAINER_LOAD" on the OCEAN_CARRIER arrangement.
FREIGHT_FORWARDER
Identifies the freight forwarder managing the shipment end-to-end. Required when the FREIGHT_FORWARDER modifier is in scope; rejected when it isn't.
Fields
| Name | Type | Required | Description |
|---|---|---|---|
scac | string | Yes | Standard carrier alpha code identifying the freight forwarder. |
referenceNumber | string | Yes | Forwarder-issued reference for the shipment. Tenant-scoped identifier. |
Cargo owner arrangements
SHIPPER, CONSIGNEE, BILL_TO, and NOTIFY_PARTY identify the commercial parties associated with the shipment: who is shipping the goods, who is receiving them, who is paying, and who should be notified. All four are optional on any shipment regardless of scope, and they do not affect validation rules for carrier or forwarder arrangements.
| Arrangement type | Role |
|---|---|
SHIPPER | The party sending the goods (origin party / exporter). |
CONSIGNEE | The party receiving the goods (destination party / importer). |
BILL_TO | The party responsible for paying freight charges. |
NOTIFY_PARTY | A party to be notified of shipment milestones. |
All four types share the same details structure: a contact object and an address object.
Fields
| Name | Type | Required | Description |
|---|---|---|---|
contact.companyName | string | Yes | Name of the company or organization. |
contact.givenName | string | No | Contact person's first name. |
contact.familyName | string | No | Contact person's last name. |
contact.phoneNumber | string | No | Landline phone number. |
contact.mobilePhoneNumber | string | No | Mobile phone number. |
contact.email | string | No | Contact email address. |
address.addressLines | array of strings | Yes | Street address lines. |
address.postalCode | string | No | Postal / ZIP code. |
address.city | string | Yes | City name. |
address.state | string | No | State or province code. |
address.country | string | Yes | ISO 3166-1 alpha-2 country code (e.g., US, DE, GB). |
See the FCL example with a consignee arrangement for a concrete usage.
Shipment type configurations
Each primary scope tag has specific validation requirements. The following sections describe what each configuration requires and how to set it up. For end-to-end walkthroughs that pair each common business case with its legacy and unified-arrangements payloads, see Business scenarios.
Full Container Load (FCL)
FCL shipments use the OCEAN_FULL_CONTAINER_LOAD primary scope tag.
Requirements:
- An
OCEAN_CARRIERarrangement with theCONTRACTUALrole is required unlessFREIGHT_FORWARDERis in scope. - Each entry in
relatedShipmentsmust include exactly oneCONTAINER_IDidentifier.
Example: standard FCL shipment
{ "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MSCU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "MSCUAB123456", "bookingType": "FULL_CONTAINER_LOAD" } } } ] } }
Example: FCL with freight forwarder
When FREIGHT_FORWARDER is in scope, the OCEAN_CARRIER arrangement becomes optional: the forwarder can enrich carrier details later through their connection.
{ "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "KHNN", "referenceNumber": "FREF-KHNN-2026-001" } } } ] } }
→ 202 Accepted: Forwarder-only FCL shipment. The ocean carrier can be enriched by the forwarder later.
Example: FCL shipment with a consignee arrangement
{ "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MSCU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "MSCUAB123456", "bookingType": "FULL_CONTAINER_LOAD" } } }, { "type": "CONSIGNEE", "details": { "contact": { "companyName": "Riverstone Imports Ltd", "email": "logistics@riverstone-example.com", "phoneNumber": "+1-312-555-0199" }, "address": { "addressLines": ["420 Harbor Blvd"], "city": "Chicago", "country": "US" } } } ] } }
Example: FCL shipment with related container shipments
Use relatedShipments to associate individual container shipments with the parent shipment at creation time. Each entry must include exactly one CONTAINER_ID identifier; additional identifiers of other types (for example, CUSTOMER_REFERENCE) are permitted.
{ "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MSCU", "bookingNumber": "BN-MSCU-2026-001", "serviceType": "CONTAINER_YARD_TO_CONTAINER_YARD" } } } ] }, "relatedShipments": [ { "identifiers": [{ "type": "CONTAINER_ID", "value": "MSCU4546646" }] }, { "identifiers": [{ "type": "CONTAINER_ID", "value": "MSCU7832104" }] } ] }
Single-container FCL
Add the OCEAN_SINGLE_CONTAINER modifier to restrict tracking to a single container within an FCL shipment. This suppresses the discovery of sibling containers.
Requirements:
OCEAN_SINGLE_CONTAINERcan only be used withOCEAN_FULL_CONTAINER_LOAD.- A
containerNumberis required on everyOCEAN_CARRIERarrangement. - Container numbers must be consistent across all arrangements.
Example:
{ "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD", "OCEAN_SINGLE_CONTAINER"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "OOLU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "OOLUSG987654", "containerNumber": "OOLU4567890", "bookingType": "FULL_CONTAINER_LOAD" } } } ] } }
Less-than-Container Load (LCL)
LCL shipments use the OCEAN_LESS_THAN_CONTAINER_LOAD primary scope tag.
Standard LCL requirements:
- An
OCEAN_CARRIERarrangement with theCONTRACTUALrole is required unlessFREIGHT_FORWARDERis in scope. - The
COLOADERrole is supported. If a carrier's role is uncertain, the API can auto-resolve it from master data.
Example: standard LCL
{ "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "HLCU", "roles": ["CONTRACTUAL"], "bookingNumber": "HLCU-BK-20260415-001", "bookingType": "LESS_THAN_CONTAINER_LOAD" } } } ] } }
Example: standard LCL with a freight-forwarder arrangement
Adding FREIGHT_FORWARDER to a standard LCL shipment makes the forwarder the required party; the OCEAN_CARRIER arrangement becomes optional and can be enriched by the forwarder later. The bookingType stays LESS_THAN_CONTAINER_LOAD: that is what distinguishes this from an LCL consolidation, which uses bookingType: FULL_CONTAINER_LOAD.
{ "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "KHNN", "referenceNumber": "FREF-KHNN-2026-LCL-001" } } }, { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "HLCU", "roles": ["CONTRACTUAL"], "bookingNumber": "HLCU-BK-20260415-001", "bookingType": "LESS_THAN_CONTAINER_LOAD" } } } ] } }
LCL consolidation variant:
For freight-forwarder-managed LCL consolidations, where the forwarder books a full container and consolidates multiple LCL shipments inside it, use:
scope: ["OCEAN_LESS_THAN_CONTAINER_LOAD", "FREIGHT_FORWARDER"] bookingType: FULL_CONTAINER_LOAD
The consolidation case is the one exception to the otherwise-optional FREIGHT_FORWARDER modifier: it is always required for consolidation.
Additional requirements for the consolidation variant:
- A
FREIGHT_FORWARDERarrangement is always required. - If an
OCEAN_CARRIERarrangement is provided, it must include acontainerNumberandbookingType: FULL_CONTAINER_LOAD. - This variant requires the freight-forwarder permission on your tenant.
Example: LCL consolidation
{ "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "EXFU", "referenceNumber": "EXFU-CONSOL-2026-0881" } } }, { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "EGLV", "roles": ["CONTRACTUAL"], "containerNumber": "EGLV3398812", "bookingType": "FULL_CONTAINER_LOAD" } } } ] } }
Roll-on/Roll-off (RoRo)
RoRo shipments use the OCEAN_ROLL_ON_ROLL_OFF primary scope tag.
Requirements:
- An
OCEAN_CARRIERarrangement with theCONTRACTUALrole is required unlessFREIGHT_FORWARDERis in scope. - Each entry in
relatedShipmentsmust include exactly oneVEHICLE_IDENTIFICATION_NUMBER(VIN) identifier. - The
FREIGHT_FORWARDERmodifier is supported.
Example: standard RoRo shipment
{ "plan": { "scope": ["OCEAN_ROLL_ON_ROLL_OFF"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "WALM", "roles": ["CONTRACTUAL"], "bookingNumber": "WALM-RORO-2026-00447", "bookingType": "ROLL_ON_ROLL_OFF" } } } ] } }
Example: RoRo with freight forwarder
{ "plan": { "scope": ["OCEAN_ROLL_ON_ROLL_OFF", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "KUBE", "referenceNumber": "FREF-KUBE-2026-003" } } } ] } }
→ 202 Accepted: Forwarder-only RoRo shipment. The ocean carrier can be enriched by the forwarder later.
Example: RoRo shipment with related vehicle shipments
Use relatedShipments to associate individual vehicle shipments with the parent shipment at creation time. Each entry must include exactly one VEHICLE_IDENTIFICATION_NUMBER identifier; additional identifiers of other types (for example, CUSTOMER_REFERENCE) are permitted.
{ "plan": { "scope": ["OCEAN_ROLL_ON_ROLL_OFF"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "WALM", "bookingNumber": "WALM-RORO-2026-00447" } } } ] }, "relatedShipments": [ { "identifiers": [ { "type": "VEHICLE_IDENTIFICATION_NUMBER", "value": "1HGCM82633A004352" } ] }, { "identifiers": [ { "type": "VEHICLE_IDENTIFICATION_NUMBER", "value": "WVWZZZ3CZWE123456" } ] } ] }
Business scenarios
Ocean tracking spans a wide range of move types and party rosters: full container loads booked directly with a carrier, less-than-container loads moved through a coloader, freight-forwarder-managed consolidations across multiple suppliers and consignees, and roll-on/roll-off shipments. Previously, every scenario used the same payload shape on POST /api/v4/shipments/tracking: a flat identifier list, from which the platform derived shipment type, party roles, and validation rules. The model had three limitations: ambiguous payloads defaulted silently to baseline behavior; validation errors surfaced during tracking rather than at creation; and configurations such as multi-carrier roles, coloaders, and explicit consolidation could not be expressed.
plan.scope and plan.arrangements make these signals explicit. Each of the following scenarios pairs the legacy flat-identifier payload with its scope-driven equivalent to support a one-by-one migration of an existing integration.
Not yet supported: Customs broker and dray-provider arrangement types are not yet available in this API. The following scenarios list these parties in their "Typical parties" rosters for completeness, but they cannot be modeled in the arrangements payload today.
Scenario 1: FCL booked directly with the ocean carrier
Business case: A shipper has a direct contract with an ocean carrier for a full container load. The carrier handles the international leg and, depending on the service type, any inland legs the contract covers.
Typical parties: Shipper, Ocean Carrier. Customs brokers and dray providers at either end are tracked separately.
- Primary scope:
OCEAN_FULL_CONTAINER_LOAD - Modifier scope: none
- Key arrangements:
OCEAN_CARRIER
Service type variants: This single scenario covers every door / yard / rail-ramp combination of the carrier's contract. Pick the serviceType value from the Service types reference that matches your carrier's contract.
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "CARRIER_SCAC", "value": "MAEU" }, { "type": "BILL_OF_LADING", "value": "BOL-MAEU-2026-001" } ] } | { "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "BOL-MAEU-2026-001", "serviceType": "CONTAINER_YARD_TO_CONTAINER_YARD", "bookingType": "FULL_CONTAINER_LOAD" } } } ] } } |
Previously, FCL was derived from the combination of CARRIER_SCAC and BILL_OF_LADING, and the identifier-based payload was not validated at create time. serviceType had no first-class slot, so D&D logic relied on tenant defaults or carrier inference.
What changed: Intent is explicit (OCEAN_FULL_CONTAINER_LOAD), carrier role is named (CONTRACTUAL), and serviceType carries the move-type contract for D&D and milestone planning. Validation runs at create time.
Scenario 2: FCL booked through a freight forwarder
Business case: A freight forwarder manages the FCL shipment end-to-end on behalf of the shipper. The shipper's tenant tracks via the forwarder's reference; the underlying ocean carrier can be populated later by the customer or enriched by the forwarder through its own connection.
Typical parties: Shipper, Freight Forwarder, Ocean Carrier (often added by the forwarder later).
- Primary scope:
OCEAN_FULL_CONTAINER_LOAD - Modifier scope:
FREIGHT_FORWARDER - Key arrangements:
FREIGHT_FORWARDER(+ optionalOCEAN_CARRIER)
Example 1: forwarder only. When only the forwarder's house bill of lading is known at creation, the underlying ocean carrier is enriched later through the forwarder's connection. Returns 202 Accepted.
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "FFW_SCAC", "value": "KHNN" }, { "type": "HOUSE_BILL_OF_LADING", "value": "FREF-KHNN-2026-001" } ] } | { "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "KHNN", "referenceNumber": "FREF-KHNN-2026-001" } } } ] } } |
Example 2: forwarder and ocean carrier known upfront. When both the forwarder reference and the carrier's bill of lading or booking are available at creation, supply both arrangements together so tracking activates immediately on both sides.
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "FFW_SCAC", "value": "KHNN" }, { "type": "HOUSE_BILL_OF_LADING", "value": "FREF-KHNN-2026-001" }, { "type": "CARRIER_SCAC", "value": "MAEU" }, { "type": "BILL_OF_LADING", "value": "BOL-MAEU-2026-001" } ] } | { "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "KHNN", "referenceNumber": "FREF-KHNN-2026-001" } } }, { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "BOL-MAEU-2026-001", "serviceType": "CONTAINER_YARD_TO_CONTAINER_YARD", "bookingType": "FULL_CONTAINER_LOAD" } } } ] } } |
Previously, the presence of FFW_SCAC flipped the shipment into forwarder mode, but the ocean carrier still had to be supplied up front or guessed from the house bill of lading. When both FFW_SCAC and CARRIER_SCAC were present, the platform could not reliably match each identifier to the right SCAC; the same booking number sometimes ended up tracked against both the freight-forwarder and the ocean-carrier SCACs.
What changed: FREIGHT_FORWARDER in scope explicitly waives the requirement to provide an OCEAN_CARRIER arrangement at create time; the forwarder can enrich it later. When both arrangements are supplied, each identifier is scoped to its arrangement: the forwarder's reference sits inside the FREIGHT_FORWARDER entry and the carrier's bill of lading or booking number sits inside the OCEAN_CARRIER entry, removing the cross-SCAC ambiguity.
Scenario 3: Single-container FCL
Business case: The customer has the carrier's SCAC and the container number at creation time, but not the booking number or master bill of lading. OCEAN_SINGLE_CONTAINER enables tracking anchored on the container alone.
Typical parties: Same as Scenario 1.
- Primary scope:
OCEAN_FULL_CONTAINER_LOAD - Modifier scope:
OCEAN_SINGLE_CONTAINER - Key arrangements:
OCEAN_CARRIERwithcontainerNumber
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "CARRIER_SCAC", "value": "OOLU" }, { "type": "CONTAINER_ID", "value": "OOLU4567890" } ] } | { "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD", "OCEAN_SINGLE_CONTAINER"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "OOLU", "roles": ["CONTRACTUAL"], "containerNumber": "OOLU4567890", "bookingType": "FULL_CONTAINER_LOAD" } } } ] } } |
Previously, this case was supported by passing just CARRIER_SCAC and CONTAINER_ID in the flat identifier list, but the single-container intent was derived from the identifier shape rather than declared. Once the carrier connection discovered a booking or bill of lading for the container, the shipment became hard to distinguish from another shipment that had supplied a bill of lading at creation.
What changed: The OCEAN_SINGLE_CONTAINER modifier explicitly anchors tracking on the container number alone, no booking or bill of lading required. Validation enforces that containerNumber is present on the carrier arrangement. Any bill of lading or booking number discovered later by the carrier connection stays separate from plan.arrangements, so the plan remains exactly as declared at creation.
Scenario 4: FCL with related container shipments
Business case: The customer wants to declare related containers explicitly at creation time rather than wait for the project44 system to discover them from the carrier. This is useful when per-container metadata (attributes, custom references, or linked orders) needs to be attached upfront so it is available the moment tracking begins. Declared containers are additive: the system continues to discover and attach further containers later reported by the carrier or the forwarder. Use Scenario 5 if tracking should be restricted to a fixed set.
Typical parties: Shipper, Ocean Carrier, plus per-container metadata at the customer's discretion. For forwarder-managed FCL with related containers, also include a FREIGHT_FORWARDER arrangement and add FREIGHT_FORWARDER to plan.scope (same pattern as Scenario 2), the relatedShipments shape is identical.
- Primary scope:
OCEAN_FULL_CONTAINER_LOAD - Modifier scope: optional
FREIGHT_FORWARDER - Key arrangements:
OCEAN_CARRIER+relatedShipments[](+ optionalFREIGHT_FORWARDER)
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "CARRIER_SCAC", "value": "MSCU" }, { "type": "BOOKING_NUMBER", "value": "BN-MSCU-2026-001" } ], "relatedShipments": [ { "identifiers": [{ "type": "CONTAINER_ID", "value": "MSCU4546646" }], "attributes": [{ "name": "Hazardous flag", "value": "No" }] } ] } | { "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MSCU", "bookingNumber": "BN-MSCU-2026-001", "serviceType": "CONTAINER_YARD_TO_CONTAINER_YARD" } } } ] }, "relatedShipments": [ { "identifiers": [{ "type": "CONTAINER_ID", "value": "MSCU4546646" }], "attributes": [{ "name": "Hazardous flag", "value": "No" }] } ] } |
Previously, relatedShipments was supported but the parent-child relationship was derived implicitly. There was no explicit signal that the parent represented an FCL booking, and no validation that each child carried exactly one container identifier.
What changed: The parent's FCL intent is explicit, and each related shipment must include exactly one CONTAINER_ID identifier, enforced at create time.
Scenario 5: FCL with discovery policy
Business case: The booking covers many containers but the customer only wants to track a known subset, for example, a partner is tracking the rest, or only specific containers are commercially relevant. Use Scenario 4 if you want to declare known containers preemptively without restricting carrier-side discovery to that set.
Typical parties: Same as Scenario 4. discoveryPolicy and relatedShipments solve overlapping but distinct problems and can be combined: relatedShipments attaches children you already know about, while discoveryPolicy.limitTo restricts the set the platform tracks via carrier-side discovery.
- Primary scope:
OCEAN_FULL_CONTAINER_LOAD - Modifier scope: none
- Key arrangements:
OCEAN_CARRIER+plan.discoveryPolicy.limitTo
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "CARRIER_SCAC", "value": "MAEU" }, { "type": "BOOKING_NUMBER", "value": "BKG-MAEU-2026-001" }, { "type": "CONTAINER_ID", "value": "MAEU4546646" }, { "type": "CONTAINER_ID", "value": "MAEU7832104" } ] } | { "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "bookingNumber": "BKG-MAEU-2026-001" } } } ], "discoveryPolicy": { "limitTo": { "containerNumbers": ["MAEU4546646", "MAEU7832104"] } } } } |
Previously, this restriction was supported by listing the container IDs alongside the carrier SCAC and booking number in the flat identifier list; the presence of CONTAINER_ID entries told the platform which containers to track. The restriction intent was derived from the identifier shape rather than declared as a policy, and a shipment with a single-container filter sometimes got confused with another container shipment managed under a different booking.
What changed: plan.discoveryPolicy.limitTo.containerNumbers declares the restriction explicitly on the plan, separate from arrangement identifiers. A shipment with a discovery policy is now clearly distinguishable from another shipment that tracks the same containers under a different booking, and containers outside the list are ignored even if the carrier reports them.
Scenario 6: Multi-carrier FCL (NVOCC contractual + operating)
Business case: An NVOCC books capacity with an underlying operating carrier and resells it to the shipper. Both carriers play a role: the NVOCC is the contractual party (issues the master bill of lading, owns the customer relationship), and the operating carrier physically moves the container. The platform represents both as OCEAN_CARRIER arrangements distinguished by the roles field, the NVOCC carries the CONTRACTUAL role, the underlying carrier carries OPERATING. The operating carrier can be declared upfront, or the NVOCC connection can discover it and append a second OCEAN_CARRIER arrangement with role OPERATING after creation.
Typical parties: Shipper, NVOCC (contractual carrier), Operating carrier, typically reached via different booking numbers.
- Primary scope:
OCEAN_FULL_CONTAINER_LOAD - Modifier scope: none
- Key arrangements:
OCEAN_CARRIER(CONTRACTUAL) + optionalOCEAN_CARRIER(OPERATING)
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "CARRIER_SCAC", "value": "SDBJ" }, { "type": "BILL_OF_LADING", "value": "BOL-SDBJ-2026-001" } ] } | { "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "SDBJ", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "BOL-SDBJ-2026-001", "serviceType": "CONTAINER_YARD_TO_CONTAINER_YARD" } } }, { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "roles": ["OPERATING"], "bookingNumber": "BN-MAEU-2026-001" } } } ] } } |
Previously, the legacy payload exposed only one carrier; it was not even possible to declare the operating carrier's identifiers separately.
What changed: The operating carrier's identifiers can now be declared in a second OCEAN_CARRIER arrangement with role OPERATING, separate from the contractual carrier. Events from both carriers flow into the shipment with correct attribution.
Scenario 7: Roll-on/Roll-off (RoRo)
Business case: Wheeled cargo (vehicles, trailers, machinery) shipped on a RoRo vessel. Individual units are identified by Vehicle Identification Number (VIN) rather than container ID.
Typical parties: Shipper, Ocean Carrier.
- Primary scope:
OCEAN_ROLL_ON_ROLL_OFF - Modifier scope: none
- Key arrangements:
OCEAN_CARRIER(+ optionalrelatedShipments[])
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "CARRIER_SCAC", "value": "WALM" }, { "type": "BOOKING_NUMBER", "value": "WALM-RORO-2026-00447" } ], "relatedShipments": [ { "identifiers": [ { "type": "VEHICLE_IDENTIFICATION_NUMBER", "value": "1HGCM82633A004352" } ] } ] } | { "plan": { "scope": ["OCEAN_ROLL_ON_ROLL_OFF"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "WALM", "roles": ["CONTRACTUAL"], "bookingNumber": "WALM-RORO-2026-00447", "bookingType": "ROLL_ON_ROLL_OFF" } } } ] }, "relatedShipments": [ { "identifiers": [ { "type": "VEHICLE_IDENTIFICATION_NUMBER", "value": "1HGCM82633A004352" } ] } ] } |
Previously, RoRo bookings used the same flat-identifier shape as FCL. The creation payload itself could not distinguish a RoRo shipment from an FCL one. The shipment type was assigned only after tracking data arrived: VIN identifiers in the events marked the shipment as RoRo; otherwise it stayed as a regular ocean (FCL) shipment.
What changed: OCEAN_ROLL_ON_ROLL_OFF declares RoRo intent at creation time, so the shipment type is known from the start instead of being attached later from tracking data. Related shipments validate that each entry carries a VIN.
Scenario 8: Standard LCL booked with the ocean carrier
Business case: The shipper has booked LCL space directly with an ocean carrier or NVOCC. Multiple shippers share a single container and the carrier handles consolidation. bookingType: LESS_THAN_CONTAINER_LOAD is what distinguishes this scenario from an LCL consolidation, where the same scope tag appears with bookingType: FULL_CONTAINER_LOAD (see Scenarios 10–12).
Typical parties: Shipper, Ocean Carrier or NVOCC.
- Primary scope:
OCEAN_LESS_THAN_CONTAINER_LOAD - Modifier scope: none
- Key arrangements:
OCEAN_CARRIER
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "CARRIER_SCAC", "value": "HLCU" }, { "type": "BOOKING_NUMBER", "value": "HLCU-BK-20260415-001" } ] } | { "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "HLCU", "roles": ["CONTRACTUAL"], "bookingNumber": "HLCU-BK-20260415-001", "bookingType": "LESS_THAN_CONTAINER_LOAD" } } } ] } } |
Previously, the platform supported LCL only for a known list of SCACs; LCL vs FCL was derived from the SCAC value because the LCL-supporting set was small. This approach did not scale as more carriers and NVOCCs added LCL support.
What changed: LCL intent is declared upfront via plan.scope and bookingType. The system tracks the shipment as LCL regardless of which SCAC is used, removing the dependency on a curated allowlist.
Scenario 9: LCL with a coloader
Business case: A contractual NVOCC books capacity with an underlying coloader (another NVOCC) that physically operates the container. Visibility into both is required.
Typical parties: Shipper, Contractual NVOCC, Coloader NVOCC.
- Primary scope:
OCEAN_LESS_THAN_CONTAINER_LOAD - Modifier scope: none
- Key arrangements: Two
OCEAN_CARRIERarrangements (CONTRACTUAL+COLOADER)
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "CARRIER_SCAC", "value": "KHNN" }, { "type": "HOUSE_BILL_OF_LADING", "value": "HBOL-KHNN-2026-001" } ] } | { "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "KHNN", "roles": ["CONTRACTUAL"], "houseBillOfLadingNumber": "HBOL-KHNN-2026-001" } } }, { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "NAQA", "roles": ["COLOADER"], "bookingNumber": "BN-NAQA-2026-001" } } } ] } } |
Previously, the legacy payload exposed only one carrier; it was not even possible to declare the coloader's identifiers separately.
What changed: The coloader's identifiers can now be declared in a second OCEAN_CARRIER arrangement with role COLOADER, separate from the contractual carrier. Events from both NVOCCs flow into the shipment with correct attribution.
Scenario 10: Buyer's consolidation
Business case: A freight forwarder consolidates cargo from multiple suppliers at an origin CFS into one full container, ships it as an FCL, and delivers it to a destination CFS, yard, or consignee's door. On this platform, each shipment models a single supplier's cargo unit, so consolidating cargo from N suppliers into one container creates N separate shipments, all sharing the same OCEAN_CARRIER FCL booking but having different forwarder references (one per supplier cargo unit). The freight forwarder is the consolidating party, responsible for combining the cargo units into the container.
Typical parties: Multiple Suppliers, Origin FFW (consolidator), Ocean Carrier, optional destination dray and customs broker, Consignee.
- Primary scope:
OCEAN_LESS_THAN_CONTAINER_LOAD - Modifier scope:
FREIGHT_FORWARDER - Key arrangements:
FREIGHT_FORWARDER+OCEAN_CARRIER(bookingType: FULL_CONTAINER_LOAD)
Service type: CFS_TO_CONTAINER_YARD.
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "FFW_SCAC", "value": "EXFU" }, { "type": "HOUSE_BILL_OF_LADING", "value": "EXFU-CONSOL-2026-0881" }, { "type": "CARRIER_SCAC", "value": "EGLV" }, { "type": "BOOKING_NUMBER", "value": "BKG-EGLV-2026-0881" } ] } | { "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "EXFU", "referenceNumber": "EXFU-CONSOL-2026-0881" } } }, { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "EGLV", "roles": ["CONTRACTUAL"], "containerNumber": "EGLV3398812", "billOfLadingNumber": "BOL-EGLV-2026-001", "bookingType": "FULL_CONTAINER_LOAD", "serviceType": "CFS_TO_CONTAINER_YARD" } } } ] } } |
Previously, consolidation was not supported on the platform. The shipment was tracked as a standard FCL, and the LCL nature of the consolidated cargo (multiple cargo units sharing the container) could not be expressed. The container was tracked separately as a discovered container, and its movement events did not flow back onto the cargo-level shipments.
What changed: The shipment is treated as LCL, and the consolidated container's movement events are attached directly to it. Cargo-level tracking and container-level tracking are blended onto the same shipment record instead of living on two separate ones.
Scenario 11: Shipper's consolidation
Business case: A single shipper packs cargo destined for multiple consignees into one full container at origin. The container ships as an FCL, and a freight forwarder deconsolidates at a destination CFS to dispatch the cargo to each consignee. On this platform, each shipment models a single consignee-bound cargo unit, so cargo for N consignees creates N separate shipments, all sharing the same OCEAN_CARRIER FCL booking but having different forwarder references (one per consignee cargo unit). The freight forwarder's only responsibility is deconsolidation at the destination.
Typical parties: Shipper (packs the container at origin), Ocean Carrier, Destination FFW (deconsolidator), multiple Consignees.
- Primary scope:
OCEAN_LESS_THAN_CONTAINER_LOAD - Modifier scope:
FREIGHT_FORWARDER - Key arrangements:
FREIGHT_FORWARDER+OCEAN_CARRIER(bookingType: FULL_CONTAINER_LOAD)
Service type: CONTAINER_YARD_TO_CFS.
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "FFW_SCAC", "value": "EXFU" }, { "type": "HOUSE_BILL_OF_LADING", "value": "EXFU-SHCONSOL-2026-0312" }, { "type": "CARRIER_SCAC", "value": "MAEU" }, { "type": "BOOKING_NUMBER", "value": "BKG-MAEU-2026-SC-09" } ] } | { "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "EXFU", "referenceNumber": "EXFU-SHCONSOL-2026-0312" } } }, { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "roles": ["CONTRACTUAL"], "containerNumber": "MSKU7720091", "billOfLadingNumber": "BOL-MAEU-2026-SC-09", "bookingType": "FULL_CONTAINER_LOAD", "serviceType": "CONTAINER_YARD_TO_CFS" } } } ] } } |
Previously, consolidation was not supported on the platform. The shipment was tracked as a standard FCL, and the LCL nature of the consolidated cargo (multiple cargo units sharing the container) could not be expressed. The container was tracked separately as a discovered container, and its movement events did not flow back onto the cargo-level shipments.
What changed: The shipment is treated as LCL, and the consolidated container's movement events are attached directly to it. Cargo-level tracking and container-level tracking are blended onto the same shipment record instead of living on two separate ones.
Scenario 12: Consol-deconsol
Business case: A freight forwarder consolidates cargo from multiple suppliers at an origin CFS into one full container, ships it as an FCL, and deconsolidates at a destination CFS to dispatch the cargo to multiple consignees. On this platform, each shipment models a single cargo unit, so the consolidation creates one shipment per cargo unit, all sharing the same OCEAN_CARRIER FCL booking but having different forwarder references (one per cargo unit). The platform supports only one FREIGHT_FORWARDER arrangement per shipment, so the same forwarder must handle both consolidation at origin and deconsolidation at destination.
Typical parties: Multiple Suppliers, Freight Forwarder (handles both consolidation at origin and deconsolidation at destination), Ocean Carrier, multiple Consignees.
- Primary scope:
OCEAN_LESS_THAN_CONTAINER_LOAD - Modifier scope:
FREIGHT_FORWARDER - Key arrangements:
FREIGHT_FORWARDER+OCEAN_CARRIER(bookingType: FULL_CONTAINER_LOAD)
Service type: CFS_TO_CFS.
| Before this API | With unified arrangements |
|---|---|
{ "identifiers": [ { "type": "FFW_SCAC", "value": "EXFU" }, { "type": "HOUSE_BILL_OF_LADING", "value": "EXFU-CD-2026-0512" }, { "type": "CARRIER_SCAC", "value": "ONEY" }, { "type": "BOOKING_NUMBER", "value": "BKG-ONEY-2026-CD-12" } ] } | { "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "details": { "freightForwarder": { "scac": "EXFU", "referenceNumber": "EXFU-CONSOL-2026-0512" } } }, { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "ONEY", "roles": ["CONTRACTUAL"], "containerNumber": "ONEU8845210", "billOfLadingNumber": "BOL-ONEY-2026-CD-12", "bookingType": "FULL_CONTAINER_LOAD", "serviceType": "CFS_TO_CFS" } } } ] } } |
Previously, consolidation was not supported on the platform. The shipment was tracked as a standard FCL, and the LCL nature of the consolidated cargo (multiple cargo units sharing the container) could not be expressed. The container was tracked separately as a discovered container, and its movement events did not flow back onto the cargo-level shipments.
What changed: The shipment is treated as LCL, and the consolidated container's movement events are attached directly to it. Cargo-level tracking and container-level tracking are blended onto the same shipment record instead of living on two separate ones.
Linking cargo shipments via shared arrangements
When multiple cargo units share the same underlying container (as in Scenarios 10–12), each cargo unit is created as its own shipment on the platform. To link these shipments via the shared container, give each shipment's OCEAN_CARRIER arrangement the same top-level referenceId. The platform treats arrangements that share a referenceId as the same underlying entity, so the shared arrangement can be used to navigate or summarize the list of cargo units traveling inside that container. The FREIGHT_FORWARDER arrangements, on the other hand, carry distinct referenceId values per cargo unit, so each cargo unit retains its own forwarder-level identifier.
The following example shows two cargo shipments that share the same OCEAN_CARRIER arrangement (linked via referenceId: "CARRIER-EGLV-CON-001") but each carry their own FREIGHT_FORWARDER arrangement with distinct referenceId values.
| Cargo shipment 1 | Cargo shipment 2 |
|---|---|
{ "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "referenceId": "FFW-EXFU-CARGO-A-001", "details": { "freightForwarder": { "scac": "EXFU", "referenceNumber": "EXFU-CARGO-A-2026-0881" } } }, { "type": "OCEAN_CARRIER", "referenceId": "CARRIER-EGLV-CON-001", "details": { "oceanCarrier": { "scac": "EGLV", "roles": ["CONTRACTUAL"], "containerNumber": "EGLV3398812", "billOfLadingNumber": "BOL-EGLV-2026-001", "bookingType": "FULL_CONTAINER_LOAD", "serviceType": "CFS_TO_CONTAINER_YARD" } } } ] } } | { "plan": { "scope": ["OCEAN_LESS_THAN_CONTAINER_LOAD", "FREIGHT_FORWARDER"], "arrangements": [ { "type": "FREIGHT_FORWARDER", "referenceId": "FFW-EXFU-CARGO-B-001", "details": { "freightForwarder": { "scac": "EXFU", "referenceNumber": "EXFU-CARGO-B-2026-0881" } } }, { "type": "OCEAN_CARRIER", "referenceId": "CARRIER-EGLV-CON-001", "details": { "oceanCarrier": { "scac": "EGLV", "roles": ["CONTRACTUAL"], "containerNumber": "EGLV3398812", "billOfLadingNumber": "BOL-EGLV-2026-001", "bookingType": "FULL_CONTAINER_LOAD", "serviceType": "CFS_TO_CONTAINER_YARD" } } } ] } } |
Because the two OCEAN_CARRIER arrangements share the same referenceId, the platform resolves them to one underlying entity. Tracking still happens independently on each cargo shipment; the shared arrangement just provides a navigable link between them, useful for summarizing the cargo units traveling in the container. The FREIGHT_FORWARDER arrangements stay distinct because each carries a different referenceId, so each cargo unit retains its own forwarder-level identifier. See Arrangement behavior on POST and PUT for the full matching rules around referenceId.
Endpoints
The following endpoints are available for ocean shipment tracking.
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 <your-api-key> |
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. |
plan.scope | array of strings | Yes | Declares the shipment type. The first element is the primary scope tag. See Scope. |
plan.arrangements | array of objects | Yes | Declares participating parties and their tracking identifiers. See Arrangement types. |
relatedShipments | array of objects | No | Child shipments linked to this parent. Maximum 250 items. Each related shipment must not include its own arrangements. |
attributes | object | No | User-defined custom attributes to associate with the shipment. |
entitledAccessGroups | array | No | Access groups entitled to visibility of this shipment. |
Response fields
| Name | Type | Description |
|---|---|---|
id | string | UUID of the created or updated shipment. |
identifiers | array | Logistics identifiers associated with the shipment. |
plan | object | The shipment plan, including scope and arrangements. |
routeInfo | object | Stops and route segments for the shipment. |
relatedShipments | array | Child shipments linked to this parent. |
shipmentShareLink | string | Publicly accessible link to the shipment details page. |
attributes | object | Custom attributes associated with the shipment. |
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. |
Request example
{ "identifiers": [ { "type": "BILL_OF_LADING", "value": "BOL-MAEU-2026-001" }, { "type": "CARRIER_SCAC", "value": "MAEU" } ], "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "BOL-MAEU-2026-001", "bookingNumber": "BKG-98765", "serviceType": "CONTAINER_YARD_TO_CONTAINER_YARD", "bookingType": "FULL_CONTAINER_LOAD" } } } ] } }
Response example
{ "id": "3b6bff66-c74b-475c-a222-ce6a92413650", "identifiers": [ { "type": "BILL_OF_LADING", "value": "BOL-MAEU-2026-001" }, { "type": "CARRIER_SCAC", "value": "MAEU" } ], "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD"], "arrangements": [ { "id": "arr-11223344-5566-7788-99aa-bbccddeeff00", "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "BOL-MAEU-2026-001", "bookingNumber": "BKG-98765", "serviceType": "CONTAINER_YARD_TO_CONTAINER_YARD", "bookingType": "FULL_CONTAINER_LOAD" } } } ] }, "shipmentShareLink": "https://movement.project44.com/share/abc123xyz", "createdDateTime": "2026-04-23T09:15:00Z", "lastModifiedDateTime": "2026-04-23T09:15:00Z" }
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. |
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 headers
| Name | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | Bearer token. Format: Bearer <your-api-key> |
Content-Type | string | Yes | Must be application/json |
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.scope | array of strings | Yes | Full scope declaration for the shipment. Replaces the existing scope. |
plan.arrangements | array of objects | Yes | Full list of arrangements. Any arrangement not included here is unlinked. |
See Arrangement behavior on POST and PUT for the full matching and update rules.
Request example
{ "id": "3b6bff66-c74b-475c-a222-ce6a92413650", "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD", "OCEAN_SINGLE_CONTAINER"], "arrangements": [ { "id": "arr-11223344-5566-7788-99aa-bbccddeeff00", "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "BOL-MAEU-2026-001", "containerNumber": "MSKU1234567", "bookingType": "FULL_CONTAINER_LOAD" } } } ] } }
Response example
{ "id": "3b6bff66-c74b-475c-a222-ce6a92413650", "identifiers": [{ "type": "BILL_OF_LADING", "value": "BOL-MAEU-2026-001" }], "plan": { "scope": ["OCEAN_FULL_CONTAINER_LOAD", "OCEAN_SINGLE_CONTAINER"], "arrangements": [ { "id": "arr-11223344-5566-7788-99aa-bbccddeeff00", "type": "OCEAN_CARRIER", "details": { "oceanCarrier": { "scac": "MAEU", "roles": ["CONTRACTUAL"], "billOfLadingNumber": "BOL-MAEU-2026-001", "containerNumber": "MSKU1234567", "bookingType": "FULL_CONTAINER_LOAD" } } } ] }, "lastModifiedDateTime": "2026-04-24T14:30:00Z" }
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 Arrangement types 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, 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.