project44
Migrate from project44 to TimeToCargo API v2: authentication, requests and response handling
This guide covers ocean tracking migration from project44 Unified API and Ocean API v4 to TimeToCargo API v2. Unified and Ocean operations are labelled separately because their parameters and responses differ.
See the migration workflow for importing references and setting up notifications. Full TimeToCargo schemas are in the API reference.
Authentication
project44 uses OAuth client credentials and the Authorization: Bearer … header. When exporting source data, keep the project44 regional host configured for your account: NA or EU.
To switch to TimeToCargo:
- Create a key in your TimeToCargo account.
- Change the base URL to
https://tracking.timetocargo.com/v2. For example, the full tracking endpoint ishttps://tracking.timetocargo.com/v2/tracking. - Send the new key in the
api_keyquery parameter of every request, including batch tasks and webhook management. Keys and tokens from the former provider cannot be reused.
See authentication for key usage and request limits.
Tracking records and identifiers
| project44 | TimeToCargo | Required change |
|---|---|---|
Unified shipment.id / Ocean masterShipmentId | shipment.id (partial) | Create tracking for a container, bill of lading or booking reference. Map the new ID to the source masterShipmentId. |
Ocean oceanLegId | shipment.id + your leg mapping | Keep oceanLegId alongside the source masterShipmentId and new tracking ID. The new API does not create a separate ocean-leg object. |
equipmentIdentifier / identifiers | track_number + containers[] | Choose the primary reference for the request. Handle returned container numbers separately from internal project44 IDs. |
events / positions / states | events[] / route_info / shipment_status | Read events, the current position and progress from separate fields. Export full position and state histories before switching off project44. |
Operation mapping
| project44 | TimeToCargo | Required change |
|---|---|---|
Unified: POST /api/v4/shipments/tracking | POST /v2/tracking/batch → GET /v2/tracking/batch | Submit primary references in a batch, retrieve the task result, and save permanent tracking IDs. |
Unified: PUT /api/v4/shipments/tracking | No direct equivalent | Create new tracking for a new reference. Maintain transport-plan changes and leg relationships in your own system. |
Unified: GET /api/v4/shipments/{shipmentId}/tracking | GET /v2/shipments | Fetch list pages and select the record using the new ID from your mapping table. There is no individual GET by ID. |
Unified: GET /api/v4/shipments/{shipmentId}/eventhistory, /positionhistory, /tracking/history | GET /v2/shipments?need_route=true (partial) | Use the list for current data and route information. Export project44 event, position and response history separately. |
Ocean: POST /api/v4/ocean/shipments | POST /v2/tracking/batch → GET /v2/tracking/batch | Submit the container number and SCAC. Save task_id, retrieve the result, and map the new ID to the source masterShipmentId and oceanLegId. |
Ocean: GET /api/v4/ocean/shipments/{shipmentId}/statuses | GET /v2/shipments | Find the record by its new ID and read tracked_item.data.data. Add need_route=true if you need the current position. |
Unified: DELETE /api/v4/shipments/{shipmentId}/tracking, Ocean: DELETE /api/v4/ocean/shipments/{shipmentId} | DELETE /v2/shipments | Pass new IDs in the shipmentIds array. See the lifecycle guide for stopping, archiving and deletion rules. |
GET /api/v4/shipments/{shipmentId}/attributes | note + your own store | Move one label of up to 40 characters into note. Keep other attributes in your own system. |
GET/PUT /api/v4/webhooks, GET/DELETE /api/v4/webhooks/{webhookId} | /v2/webhooks | Recreate webhooks through /v2/webhooks. Update management methods, signature checks and notification handling using the webhook guide. |
Request parameters
| project44 | TimeToCargo | Required change |
|---|---|---|
Unified identifiers: CONTAINER_ID / BILL_OF_LADING / BOOKING_NUMBER | tracked_items[].track_number.value + type | Put the selected reference in track_number.value. Map CONTAINER_ID to container, BILL_OF_LADING to bl, and BOOKING_NUMBER to bk. |
Unified identifiers: CARRIER_SCAC | tracked_items[].company | Take the CARRIER_SCAC identifier value, verify it through GET /v2/companies, and pass it in company. |
Ocean equipmentIdentifier | tracked_items[].track_number | For equipmentIdentifier.type: "CONTAINER_ID", copy its value and set track_number.type to container. |
Ocean carrierSCACCode | tracked_items[].company | Copy carrierSCACCode into company after checking the carrier through GET /v2/companies. |
Ocean includePositionData | need_route=true | Add the need_route=true query parameter. It requests the route and current point, not full AIS history. |
attributes / plan / relatedShipments / accessGroups | note + your own store | Save one label as note with a separate PATCH. Maintain the plan, shipment relationships and access groups in your system. |
Response fields
Select a record from data.data[] in the GET /v2/shipments response. Paths starting with tracked_item are relative to that record. Containers are in tracked_item.data.data.containers[], with events in each container's events[]. In GET /v2/tracking, shipment data is directly under data.
| project44 | TimeToCargo | Response handling |
|---|---|---|
Unified id / Ocean shipment.oceanLegId | data.task_id → results[].data.tracking_metadata.id | Retrieve the task result after submission and save the permanent data.results[].data.tracking_metadata.id. Map it to both source project44 IDs. |
Unified GET tracking: identifiers / plan / routeInfo | tracked_item + tracking data | In the list, read the reference from tracked_item.track_number and shipment data from tracked_item.data.data. The response does not include a copy of the project44 plan. |
Ocean trackedShipment.equipmentIdentifier.value | tracked_item.data.data.containers[].number | Find the required container by containers[].number. Do not compare the number with the internal record ID. |
Ocean trackedShipment.events[] | tracked_item.data.data.containers[].events[] | Read the code from status_code, the date from date, and the actual flag from actual. Consider the port and journey stage when mapping project44 codes. |
Ocean trackedShipment.stops / vessels | summary / locations / events[].vessel | Resolve the event's location through locations. Read the vessel name from the event's own vessel string. |
Ocean trackedShipment.positions / states | route_info / shipment_status | Use route_info.current_position for the current point and shipment_status for progress. Full positions and states history arrays are not provided. |
Ocean shipmentId | masterShipmentId in the source system | In source data, use the current masterShipmentId instead of deprecated shipmentId. Neither replaces the new tracking ID. |
Response examples
Each pair labels the former-provider and TimeToCargo responses. Expand the longer JSON examples when you need them; the mapping tables remain visible. These are shortened examples with illustrative data. The path above each JSON block identifies the part shown. Paths in the following table start from that part.
Unified: identifiers and custom label
project44 — GET /api/v4/shipments/{shipmentId}/tracking
Shortened response.
{
"id": "33333333-3333-4333-8333-333333333333",
"identifiers": [
{
"type": "CONTAINER_ID",
"value": "MSCU1234567"
},
{
"type": "CARRIER_SCAC",
"value": "MSCU"
},
{
"type": "BOOKING_NUMBER",
"value": "BOOKING123"
}
],
"attributes": [
{
"name": "order",
"values": ["Order 123"]
}
],
"relatedShipments": [
{
"id": "66666666-6666-4666-8666-666666666666"
}
]
}TimeToCargo — GET /v2/shipments
Fragment: data.data[0].
{
"id": "11111111-1111-4111-8111-111111111111",
"status": "ACTIVE",
"note": "Order 123",
"track_number": "MSCU1234567",
"tracked_item": {
"track_number": "MSCU1234567",
"track_number_type": "container",
"data": {
"success": true,
"status": "OK",
"data": {
"summary": {
"company": {
"name": "MSC",
"scacs": ["MEDU", "MSCU"]
}
},
"containers": [
{
"number": "MSCU1234567"
}
],
"references": {
"booking_numbers": ["BOOKING123"]
}
}
}
}
}How to map
| project44 | TimeToCargo | Required change |
|---|---|---|
id = "33333333-3333-4333-8333-333333333333" | id = "11111111-1111-4111-8111-111111111111" | Map the source ID to the new one. Use the new id for tracking management. |
identifiers[0].value = "MSCU1234567" | tracked_item.track_number = "MSCU1234567" | Keep the number unchanged and set the type to container. In the list, the number is in tracked_item.track_number. |
identifiers[1].value = "MSCU" | tracked_item.data.data.summary.company.scacs = ["MEDU","MSCU"] | Read carrier codes from the scacs array and check whether it contains the required code. |
identifiers[2].value = "BOOKING123" | tracked_item.data.data.references.booking_numbers = ["BOOKING123"] | An additional booking reference may appear in references.booking_numbers. It does not change the primary tracking type from container to bk. |
attributes[0].values[0] = "Order 123" | note = "Order 123" | Save Order 123 as note after obtaining the permanent ID. Move other attributes to your database. |
relatedShipments | No direct equivalent | Maintain links to other shipments locally. The new response has no relatedShipments array. |
Ocean: event, port, vessel and state
project44 — GET /api/v4/ocean/shipments/{shipmentId}/statuses
Fragment: trackedShipment.
{
"masterShipmentId": "33333333-3333-4333-8333-333333333333",
"oceanLegId": "44444444-4444-4444-8444-444444444444",
"equipmentIdentifier": {
"type": "CONTAINER_ID",
"value": "MSCU1234567"
},
"carrierSCACCode": "MSCU",
"events": [
{
"eventCode": "LOADED",
"timeStamp": "2026-10-01T04:00:00Z",
"stopNumber": 10,
"vesselNumber": 5
}
],
"stops": [
{
"stopNumber": 10,
"stopType": "PORT_OF_LOADING",
"name": "Singapore",
"portLocode": "SGSIN",
"geoCoordinates": {
"latitude": 1.25,
"longitude": 103.75
}
}
],
"vessels": [
{
"vesselNumber": 5,
"name": "EXAMPLE VESSEL",
"imo": 9000003
}
],
"states": [
{
"stateCode": "ON_WATER",
"startTime": "2026-10-01T06:00:00Z",
"vesselNumber": 5
}
]
}TimeToCargo — GET /v2/shipments
Fragment: data.data[0].tracked_item.data.data.
{
"shipment_status": "IN_TRANSIT",
"summary": {
"company": {
"name": "MSC",
"scacs": ["MEDU", "MSCU"]
}
},
"locations": [
{
"id": 0,
"name": "Singapore",
"country": "Singapore",
"lat": 1.25,
"lng": 103.75
}
],
"containers": [
{
"number": "MSCU1234567",
"events": [
{
"location": 0,
"status": "Loaded",
"status_code": "CLL",
"date": "2026-10-01T12:00:00",
"actual": true,
"vessel": "EXAMPLE VESSEL"
}
]
}
]
}How to map
| project44 | TimeToCargo | Required change |
|---|---|---|
equipmentIdentifier.value = "MSCU1234567" | containers[0].number = "MSCU1234567" | Select the container by containers[].number. The example number remains MSCU1234567. |
events[0].eventCode = "LOADED" | containers[0].events[0].status_code = "CLL" | Here, LOADED occurs at PORT_OF_LOADING, so it corresponds to CLL. Transshipment loading requires a different code. |
events[0].stopNumber = 10 | containers[0].events[0].location = 0 | Instead of looking up stopNumber, find the locations entry whose id equals event.location. |
events[0].vesselNumber = 5 | containers[0].events[0].vessel = "EXAMPLE VESSEL" | Read the vessel name directly from event.vessel. A separate lookup by vesselNumber is no longer needed. |
states[0].stateCode = "ON_WATER" | shipment_status = "IN_TRANSIT" | For the journey shown, use shipment_status: "IN_TRANSIT". Retain the states[] history separately. |
events[0].timeStamp = "2026-10-01T04:00:00Z" | containers[0].events[0].date = "2026-10-01T12:00:00" | In this example, 04:00 UTC corresponds to 12:00 in Singapore. Compare dates using time zones, not just timestamp strings. |
Ocean: position history and current point
project44 — GET /api/v4/ocean/shipments/{shipmentId}/statuses?includePositionData=true
Fragment: trackedShipment.
{
"positions": [
{
"geoCoordinates": {
"latitude": 1.2,
"longitude": 103.7
},
"timeStamp": "2026-10-03T08:30:00Z",
"vesselNumber": 5
},
{
"geoCoordinates": {
"latitude": 1.25,
"longitude": 103.75
},
"timeStamp": "2026-10-03T09:00:00Z",
"vesselNumber": 5
}
]
}TimeToCargo — GET /v2/shipments?need_route=true
Fragment: data.data[0].tracked_item.data.data.route_info.
{
"current_position": [1.25, 103.75],
"last_updated": "2026-10-03T09:00:00"
}How to map
| project44 | TimeToCargo | Required change |
|---|---|---|
positions[1].geoCoordinates | current_position = [1.25,103.75] | Read the current point as a [latitude, longitude] array. Do not expect a full position history array. |
positions[1].timeStamp = "2026-10-03T09:00:00Z" | last_updated = "2026-10-03T09:00:00" | last_updated refers to the current position update, not the entire route geometry. Retain the original project44 timestamp: the position source and time zone may differ. |
positions | No direct equivalent | Export past project44 positions separately. route[].path describes route geometry, not a log of AIS measurements. |
Data to retain separately
Keep the source masterShipmentId and oceanLegId alongside the new tracking ID. Migrate transport plans, leg relationships, predictions, position history and access rules separately. Account for the journey stage when handling events: the example maps LOADED to CLL because loading occurs at the origin port.
Last updated on