Last updated

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/tracking endpoint 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

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 is
  • arrangements: 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:

TagShipment type
OCEAN_FULL_CONTAINER_LOADFull Container Load (FCL)
OCEAN_LESS_THAN_CONTAINER_LOADLess-than-Container Load (LCL)
OCEAN_ROLL_ON_ROLL_OFFRoll-on/roll-off (RoRo) wheeled cargo

Modifier scope tags:

TagApplies toDescription
FREIGHT_FORWARDERFCL, LCL, RoRoMarks the shipment as freight-forwarder-managed. Requires a FREIGHT_FORWARDER arrangement and an additional product permission.
OCEAN_SINGLE_CONTAINERFCL onlyRestricts 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 line
  • FREIGHT_FORWARDER: the freight forwarder managing the shipment
  • SHIPPER: the cargo owner sending the goods
  • CONSIGNEE: the cargo owner receiving the goods
  • BILL_TO: the party responsible for freight charges
  • NOTIFY_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:

NameTypeRequiredDescription
idstringNoPlatform-generated UUID assigned when the arrangement is first created. Returned in the response. Provide this on subsequent requests to update the same arrangement.
referenceIdstringNoCustomer-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

NameTypeRequiredDescription
scacstringYesStandard carrier alpha code identifying the ocean carrier.
rolesarray of stringsNoCarrier roles. Accepted values: CONTRACTUAL, OPERATING, COLOADER. Defaults to ["CONTRACTUAL"] when omitted.
billOfLadingNumberstringNoMaster bill of lading number issued by the carrier.
bookingNumberstringNoBooking number for the shipment.
houseBillOfLadingNumberstringNoHouse bill of lading number.
containerNumberstringConditionalRequired when OCEAN_SINGLE_CONTAINER is in scope. All OCEAN_CARRIER arrangements must use the same container number.
serviceTypestringNoExtent of carrier service. See Service types.
bookingTypestringNoType 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.

ValueOriginDestinationDescription
DOOR_TO_DOORDoor (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_RAMPDoor (shipper's premises)Rail rampCarrier picks up from shipper's door and delivers to a destination rail ramp.
DOOR_TO_CONTAINER_YARDDoor (shipper's premises)Container yardCarrier picks up from shipper's door and delivers to a destination container yard.
RAIL_RAMP_TO_DOORRail rampDoor (consignee's premises)Carrier picks up from an origin rail ramp and delivers to consignee's door.
RAIL_RAMP_TO_RAIL_RAMPRail rampRail rampCarrier handles rail and ocean legs between ramps.
RAIL_RAMP_TO_CONTAINER_YARDRail rampContainer yardCarrier picks up from an origin rail ramp and delivers to a destination container yard.
CONTAINER_YARD_TO_DOORContainer yardDoor (consignee's premises)Carrier picks up from an origin container yard and delivers to consignee's door.
CONTAINER_YARD_TO_RAIL_RAMPContainer yardRail rampCarrier picks up from an origin container yard and delivers to a destination rail ramp.
CONTAINER_YARD_TO_CONTAINER_YARDContainer yardContainer yardCarrier handles transport between yards including the ocean leg.
CFS_TO_CFSContainer Freight Station (CFS)Container Freight Station (CFS)Typically used for LCL shipments where cargo is consolidated and deconsolidated at freight stations.
CFS_TO_CONTAINER_YARDContainer Freight Station (CFS)Container yardConsolidation scenarios where cargo is consolidated at origin CFS.
CONTAINER_YARD_TO_CFSContainer yardContainer 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.

ValueDescription
FULL_CONTAINER_LOADThe 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_LOADThe 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_OFFThe 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 tagDefault bookingType
OCEAN_FULL_CONTAINER_LOADFULL_CONTAINER_LOAD
OCEAN_LESS_THAN_CONTAINER_LOADLESS_THAN_CONTAINER_LOAD
OCEAN_ROLL_ON_ROLL_OFFROLL_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

NameTypeRequiredDescription
scacstringYesStandard carrier alpha code identifying the freight forwarder.
referenceNumberstringYesForwarder-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 typeRole
SHIPPERThe party sending the goods (origin party / exporter).
CONSIGNEEThe party receiving the goods (destination party / importer).
BILL_TOThe party responsible for paying freight charges.
NOTIFY_PARTYA party to be notified of shipment milestones.

All four types share the same details structure: a contact object and an address object.

Fields

NameTypeRequiredDescription
contact.companyNamestringYesName of the company or organization.
contact.givenNamestringNoContact person's first name.
contact.familyNamestringNoContact person's last name.
contact.phoneNumberstringNoLandline phone number.
contact.mobilePhoneNumberstringNoMobile phone number.
contact.emailstringNoContact email address.
address.addressLinesarray of stringsYesStreet address lines.
address.postalCodestringNoPostal / ZIP code.
address.citystringYesCity name.
address.statestringNoState or province code.
address.countrystringYesISO 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_CARRIER arrangement with the CONTRACTUAL role is required unless FREIGHT_FORWARDER is in scope.
  • Each entry in relatedShipments must include exactly one CONTAINER_ID identifier.

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_CONTAINER can only be used with OCEAN_FULL_CONTAINER_LOAD.
  • A containerNumber is required on every OCEAN_CARRIER arrangement.
  • 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_CARRIER arrangement with the CONTRACTUAL role is required unless FREIGHT_FORWARDER is in scope.
  • The COLOADER role 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_FORWARDER arrangement is always required.
  • If an OCEAN_CARRIER arrangement is provided, it must include a containerNumber and bookingType: 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_CARRIER arrangement with the CONTRACTUAL role is required unless FREIGHT_FORWARDER is in scope.
  • Each entry in relatedShipments must include exactly one VEHICLE_IDENTIFICATION_NUMBER (VIN) identifier.
  • The FREIGHT_FORWARDER modifier 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 APIWith 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 (+ optional OCEAN_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 APIWith 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 APIWith 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_CARRIER with containerNumber
Before this APIWith 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.

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[] (+ optional FREIGHT_FORWARDER)
Before this APIWith 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 APIWith 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) + optional OCEAN_CARRIER (OPERATING)
Before this APIWith 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 (+ optional relatedShipments[])
Before this APIWith 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 APIWith 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_CARRIER arrangements (CONTRACTUAL + COLOADER)
Before this APIWith 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 APIWith 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 APIWith 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 APIWith 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 1Cargo 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

NameTypeRequiredDescription
AuthorizationstringYesBearer token. Format: Bearer <your-api-key>
Content-TypestringYesMust be application/json

Request body parameters

NameTypeRequiredDescription
idstringNoproject44-assigned shipment UUID. Provide this to append data to an existing shipment.
identifiersarrayNoLogistics identifiers associated with the shipment.
plan.scopearray of stringsYesDeclares the shipment type. The first element is the primary scope tag. See Scope.
plan.arrangementsarray of objectsYesDeclares participating parties and their tracking identifiers. See Arrangement types.
relatedShipmentsarray of objectsNoChild shipments linked to this parent. Maximum 250 items. Each related shipment must not include its own arrangements.
attributesobjectNoUser-defined custom attributes to associate with the shipment.
entitledAccessGroupsarrayNoAccess groups entitled to visibility of this shipment.

Response fields

NameTypeDescription
idstringUUID of the created or updated shipment.
identifiersarrayLogistics identifiers associated with the shipment.
planobjectThe shipment plan, including scope and arrangements.
routeInfoobjectStops and route segments for the shipment.
relatedShipmentsarrayChild shipments linked to this parent.
shipmentShareLinkstringPublicly accessible link to the shipment details page.
attributesobjectCustom attributes associated with the shipment.
createdDateTimestring (date-time)Timestamp when the shipment was created. Read-only.
lastModifiedDateTimestring (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

StatusDescription
400Validation failed. The response body includes a machine-readable error code and an actionable message.
401Authentication failed. Your bearer token is missing or invalid.
403Your account doesn't have the required permission for this operation.
404The shipment ID you provided doesn't exist.
500An 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

NameTypeRequiredDescription
AuthorizationstringYesBearer token. Format: Bearer <your-api-key>
Content-TypestringYesMust 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.

NameTypeRequiredDescription
idstringYesproject44-assigned shipment UUID of the shipment to replace.
plan.scopearray of stringsYesFull scope declaration for the shipment. Replaces the existing scope.
plan.arrangementsarray of objectsYesFull 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

StatusDescription
400Validation failed. The response body includes a machine-readable error code and an actionable message.
401Authentication failed. Your bearer token is missing or invalid.
403Your account doesn't have the required permission for this operation.
404The shipment ID you provided doesn't exist.
500An 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 (or referenceId) 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 requestBehavior
field absent or nullNo-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 details fully replaced by the request's details (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 referenceId to a different non-null value.
  • Setting a referenceId on a stored arrangement whose referenceId is null (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 id and referenceId where they resolve to different stored arrangements (identity conflict).
  • Supplying an id that 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.