Data Source Attribution via API

OpenTrack's API provides detailed insight into the origin of key data points like ETAs and milestones. This guide explains how to access and interpret that attribution data.

🚧

Experimental Feature

This is an early-access feature. We’re actively gathering feedback and may adjust implementation details. Please contact [email protected] to share your thoughts or request access.

Introduction

With Data Source Attribution via API, you can enrich your milestone data with metadata that shows where each data point came from. This adds transparency and trust to the visibility you provide.

✅ To enable this feature, contact OpenTrack Customer Success at ([email protected]).

Use Cases

You may benefit from Data Source Attribution if you need:

1. ETA Explainability

For both vessel and rail ETAs, the attribution metadata allows you to:

  • Understand where an ETA originated and why OpenTrack selected it
  • Compare alternative ETAs reported by multiple sources

For example, if a container’s ETA is reported by three sources:

  1. Ocean carrier: 05/05
  2. Terminal: 05/02
  3. Vessel AIS: 05/03

You’ll be able to view all of these predictions, see which one OpenTrack chose, and understand why it was selected in the timeline.

2. Milestone Source Attribution

Every milestone also includes information about which source provided it—whether it’s a carrier, terminal, rail operator, or other system.

Why It Matters: Building Trust in Visibility Data

Attribution makes your visibility data more actionable and credible. Rather than manually verifying milestone data on carrier sites, your users can immediately see the original source of each update.

This is especially valuable for ETAs, which drive time-sensitive logistics workflows. By exposing the data source, you provide confidence that it’s accurate and trustworthy.

Using Data Source Attribution via API

Milestone Metadata

The history and timeline arrays in the API contain milestone objects that represent key events in a container’s journey. When attribution is enabled, each milestone will include a metadata object with source details.

❓ Learn more about the difference between history and timeline in OpenTrack’s API.

  • historyincludes only past (actual) milestones.
  • timelineincludes both past and upcoming (estimated) milestones.

Metadata Object Properties

The contents of the metadata object will vary depending on whether the milestone is historical or projected. A detailed breakdown of metadata properties is included below.

PropertyTypeFound inDescription
source.nameStringAll milestonesIdentifier of the data source (e.g., COSCO, OOCL)
source.typeStringAll milestonesCategory of the data source (e.g., steamshipLine, terminal)
candidatesArraySpecific ESTIMATED milestonesCollection of alternative predictions from different sources
candidates[].source.nameStringSpecific ESTIMATED milestonesIdentifier of the prediction source (e.g., COSCO, OOCL)
candidates[].source.typeStringSpecific ESTIMATED milestonesCategory of the prediction source (e.g., steamshipLine, terminal)
candidates[].valueStringSpecific ESTIMATED milestonesISO 8601 timestamp of the predicted event
candidates[].isSelectedBooleanSpecific ESTIMATED milestonesFlag indicating if this is the selected prediction
selectionReasoning.summaryStringSpecific ESTIMATED milestonesHuman-readable explanation for why a prediction was selected

Data Source Types

Possible values of source.typeDescription
steamshipLineOcean carrier
terminalTerminal at an ocean port
railRail carrier
railincRailinc (rail data aggregator)
aisSatellite AIS source (vessel GPS)
opentrackOpenTrack Intelligence

Estimated Milestones

Only specific estimated milestones will include candidates and selectionReasoning metadata:

  • VESSEL_BERTHED - corresponding to Mother Vessel ETA
  • ARRIVED_AT_RAIL_FACILITY - corresponding to Rail ETA

All other estimated milestones do not include this prediction metadata (for now), so they'll have the same metadata structure as actual milestones.

Sample Estimated Milestone

{
  "milestone": "VESSEL_BERTHED",
  "timestamp": "2025-04-19T15:00:00.000Z",
  "location": {...},
  "vessel": {...},
  "status": "ESTIMATED",
  "metadata": {
    "source": {
      "name": "COSCO",
      "type": "steamshipLine"
    },
    "candidates": [
      {
        "source": {
          "name": "COSCO",
          "type": "steamshipLine"
        },
        "value": "2025-04-19T15:00:00",
        "isSelected": true
      },
      {
        "source": {
          "name": "VIG",
          "type": "terminal"
        },
        "value": "2025-04-19T04:00:00",
        "isSelected": false
      }
    ],
    "selectionReasoning": {
      "summary": "We chose the steamship line ETA because it agreed with other predictions and it is typically more accurate at this point in the journey."
    }
  }
}

Actual Milestones

Actual milestones have a simpler metadata structure that only includes source attribution.

Sample Actual Milestone

{
  "milestone": "VESSEL_DEPARTED_FROM_TRANSSHIPMENT_PORT",
  "timestamp": "2025-03-02T21:43:00.000Z",
  "location": {...},
  "vessel": {...},
  "status": "ACTUAL",
  "metadata": {
    "source": {
      "name": "OOCL",
      "type": "steamshipLine"
    }
  }
}

Usage Examples

Displaying the Selected Prediction Source

function getSelectedSource(milestone) {
  // First try to get the source from the dedicated source object
  if (milestone.metadata?.source?.name) {
    return milestone.metadata.source.name;
  }

  // Alternatively, find the selected candidate
  if (milestone.metadata?.candidates) {
    const selected = milestone.metadata.candidates.find(
      candidate => candidate.isSelected
    );

    if (selected) {
      return selected.source.name;
    }
  }

  return "Unknown";
}

Showing Alternative Predictions

function getAlternativePredictions(milestone) {
  if (milestone.metadata?.candidates) {
    return milestone.metadata.candidates
      .filter(candidate => !candidate.isSelected)
      .map(candidate => {
        return {
          source: candidate.source.name,
          type: candidate.source.type,
          prediction: candidate.value
        };
      });
  }

  return [];
}