diff --git a/algo/ca/TrackingChain.cxx b/algo/ca/TrackingChain.cxx
index 868f32e46aef9eaa9b594bb32f91f73eec31092a..0d66393a916da6d1adf8069a60befa359125cc1b 100644
--- a/algo/ca/TrackingChain.cxx
+++ b/algo/ca/TrackingChain.cxx
@@ -55,7 +55,7 @@ void TrackingChain::Init()
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
-TrackingChain::Return_t TrackingChain::Run(Input_t recoResults)
+TrackingChain::Output_t TrackingChain::Run(Input_t recoResults)
 {
   xpu::scoped_timer t_("CA");  // TODO: pass timings to monitoring for throughput?
   fCaMonitorData.Reset();
@@ -69,14 +69,9 @@ TrackingChain::Return_t TrackingChain::Run(Input_t recoResults)
   fCaFramework.SetMonitorData(fCaMonitorData);
   fCaFramework.fTrackFinder.FindTracks();
   fCaMonitorData = fCaFramework.GetMonitorData();
-  L_(info) << "TrackingChain: Timeslice contains " << fCaMonitorData.GetCounterValue(ca::ECounter::RecoTrack)
-           << " tracks, the FindTracks routine ran " << fCaMonitorData.GetTimer(ca::ETimer::FindTracks).GetTotal()
-           << " s";
 
   // ----- Init output data --------------------------------------------------------------------------------------------
-  // FIXME: SZh 22.10.2023: Provide a class for the tracking output data (tracks, hit indices and monitor)
-  fCaMonitor.AddMonitorData(fCaMonitorData);
-  return std::make_pair(std::move(fCaFramework.fRecoTracks), fCaMonitorData);
+  return PrepareOutput();
 }
 
 // ---------------------------------------------------------------------------------------------------------------------
@@ -91,13 +86,56 @@ void TrackingChain::PrepareInput(Input_t recoResults)
   int nHitsTot = recoResults.stsHits.NElements() + recoResults.tofHits.NElements();
   L_(info) << "Tracking chain: input has " << nHitsTot << " hits";
   fCaDataManager.ResetInputData(nHitsTot);
+  faHitExternalIndices.clear();
+  faHitExternalIndices.reserve(nHitsTot);
   ReadHits<EDetectorID::Sts>(recoResults.stsHits);
   ReadHits<EDetectorID::Tof>(recoResults.tofHits);
+  faHitExternalIndices.shrink_to_fit();
   fCaDataManager.SetNhitKeys(fNofHitKeys);
-  L_(info) << "Tracking chain:" << fCaDataManager.GetNofHits() << " will be passed to the ca::Framework";
+  L_(info) << "Tracking chain:" << fCaDataManager.GetNofHits() << " hits will be passed to the ca::Framework";
   fCaFramework.ReceiveInputData(fCaDataManager.TakeInputData());
 }
 
+// ---------------------------------------------------------------------------------------------------------------------
+//
+TrackingChain::Output_t TrackingChain::PrepareOutput()
+{
+  Output_t output;
+  output.tracks = std::move(fCaFramework.fRecoTracks);
+  int nTracks   = output.tracks.size();
+
+  output.stsHitIndices.reset(nTracks);
+  output.tofHitIndices.reset(nTracks);
+
+  int trackFirstHit = 0;
+  for (int iTrk = 0; iTrk < nTracks; ++iTrk) {
+    output.stsHitIndices[iTrk].clear();
+    output.tofHitIndices[iTrk].clear();
+    int nHits = output.tracks[iTrk].fNofHits;
+    for (int iHit = 0; iHit < nHits; ++iHit) {
+      int iHitInternal = fCaFramework.GetInputData().GetHit(fCaFramework.fRecoHits[trackFirstHit + iHit]).Id();
+      const auto [detID, iPartition, iPartHit] = faHitExternalIndices[iHitInternal];
+      switch (detID) {
+        case ca::EDetectorID::Sts: output.stsHitIndices[iTrk].push_back(std::make_pair(iPartition, iPartHit)); break;
+        case ca::EDetectorID::Tof: output.tofHitIndices[iTrk].push_back(std::make_pair(iPartition, iPartHit)); break;
+        default: break;
+      }
+    }
+    fCaMonitorData.IncrementCounter(ca::ECounter::RecoStsHit, output.stsHitIndices[iTrk].size());
+    fCaMonitorData.IncrementCounter(ca::ECounter::RecoTofHit, output.tofHitIndices[iTrk].size());
+    trackFirstHit += nHits;
+  }
+
+  L_(info) << "TrackingChain: Timeslice contains " << fCaMonitorData.GetCounterValue(ca::ECounter::RecoTrack)
+           << " tracks, with " << fCaMonitorData.GetCounterValue(ca::ECounter::RecoStsHit) << " sts hits, "
+           << fCaMonitorData.GetCounterValue(ca::ECounter::RecoTofHit) << " tof hits; the FindTracks routine ran "
+           << fCaMonitorData.GetTimer(ca::ETimer::FindTracks).GetTotal() << " s";
+
+  fCaMonitor.AddMonitorData(fCaMonitorData);
+  output.monitorData = fCaMonitorData;
+  return output;
+}
+
 // ---------------------------------------------------------------------------------------------------------------------
 //
 template<EDetectorID DetID>
@@ -168,6 +206,7 @@ void TrackingChain::ReadHits(PartitionedSpan<const ca::HitTypes_t::at<DetID>> hi
         lastTime = caHit.T();
         if (!IsTof) { dataStream = extHitAddress; }
         fCaDataManager.PushBackHit(caHit, dataStreamDet | dataStream);
+        faHitExternalIndices.push_back(std::make_tuple(DetID, iPartition, iPartHit));
         if constexpr (kDEBUG) {
           out << (dataStreamDet | dataStream) << " ----- " << caHit.ToString() << '\n';
           if (prevTime > caHit.T()) { out << "TIME IS UNSORTED\n"; }
diff --git a/algo/ca/TrackingChain.h b/algo/ca/TrackingChain.h
index b1338cf03aa9bdbb49027b536762a8f569ffd543..b85ff456141febabcff3cbbeb8b9dbfadfa0831a 100644
--- a/algo/ca/TrackingChain.h
+++ b/algo/ca/TrackingChain.h
@@ -32,12 +32,30 @@ namespace cbm::algo
   /// The class executes a tracking algorithm in the online data reconstruction chain.
   class TrackingChain : public SubChain {
   public:
+    /// \struct Input_t
+    /// \brief  Input to the TrackingChain
     struct Input_t {
       PartitionedSpan<sts::Hit> stsHits;
       PartitionedSpan<tof::Hit> tofHits;
     };
 
-    using Return_t = std::pair<ca::Vector<ca::Track>, ca::TrackingMonitorData>;
+    /// \struct Output_t
+    /// \brief  Output from the TrackingChain
+    struct Output_t {
+      /// \brief Reconstructed tracks
+      ca::Vector<ca::Track> tracks;
+
+      /// \brief STS hit indices
+      /// \note  Indexing: [trackID][localHit], value: (partitionID, hitIDinPartition)
+      ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>> stsHitIndices;
+
+      /// \brief TOF hit indices
+      /// \note  Indexing: [trackID][localHit], value: (partitionID, hitIDinPartition)
+      ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>> tofHitIndices;
+
+      /// \brief Monitor data
+      ca::TrackingMonitorData monitorData;
+    };
 
     /// \brief  Gets internal monitor
     const ca::TrackingMonitor& GetCaMonitor() const { return fCaMonitor; }
@@ -48,7 +66,7 @@ namespace cbm::algo
     /// \brief  Provides action for a given time-slice
     /// \param  recoResults  Structure of reconstruction results
     /// \return A pair (vector of tracks, tracking monitor)
-    Return_t Run(Input_t recoResults);
+    Output_t Run(Input_t recoResults);
 
     /// \brief  Provides action in the end of the run
     void Finalize();
@@ -61,6 +79,9 @@ namespace cbm::algo
     /// \param  recoResults  Structure of reconstruction results
     void PrepareInput(Input_t recoResults);
 
+    /// \brief  Prepares output data
+    Output_t PrepareOutput();
+
     /// \brief  Reads from different detector subsystems
     /// \tparam DetID Detector ID
     /// \param  hits  Hits vector
@@ -83,6 +104,10 @@ namespace cbm::algo
 
     ca::HitKeyIndex_t fNofHitKeys = 0;  ///< Current number of hit keys (aux)
 
+    /// \brief External indices of used hits
+    /// \note  Indexing: [globalHitID], value: (DetID, partitionID, hitID)
+    ca::Vector<std::tuple<ca::EDetectorID, uint32_t, uint32_t>> faHitExternalIndices {"faHitExternalIndices"};
+
     static constexpr bool kDEBUG = false;  ///< Debug mode
   };
 
diff --git a/algo/ca/core/utils/CaTrackingMonitor.h b/algo/ca/core/utils/CaTrackingMonitor.h
index 09c4f5408f9ee7b68a96f20ede3be0c94b8f84f4..04c3192152da23cb3f53fca962c027bd172457a8 100644
--- a/algo/ca/core/utils/CaTrackingMonitor.h
+++ b/algo/ca/core/utils/CaTrackingMonitor.h
@@ -24,6 +24,11 @@ namespace cbm::algo::ca
     RecoHitUsed,   ///< number of used reconstructed hits
     Triplet,       ///< number of triplets
     // TODO: Provide counters vs. detector ID
+    RecoMvdHit,        ///< number of MVD hits in tracks
+    RecoStsHit,        ///< number of STS hits in tracks
+    RecoMuchHit,       ///< number of MUCH hits in tracks
+    RecoTrdHit,        ///< number of TRD hits in tracks
+    RecoTofHit,        ///< number of TOF hits in tracks
     UndefinedMvdHit,   ///< number of undefined MVD hits
     UndefinedStsHit,   ///< number of undefined STS hits
     UndefinedMuchHit,  ///< number of undefined MuCh hits
@@ -61,6 +66,11 @@ namespace cbm::algo::ca
       SetCounterName(ECounter::Triplet, "triplets");
       SetCounterName(ECounter::RecoHitUsed, "used reco hits");
       SetCounterName(ECounter::SubTS, "sub-timeslices");
+      SetCounterName(ECounter::RecoMvdHit, "MVD hits in tracks");
+      SetCounterName(ECounter::RecoStsHit, "STS hits in tracks");
+      SetCounterName(ECounter::RecoMuchHit, "MUCH hits in tracks");
+      SetCounterName(ECounter::RecoTrdHit, "TRD hits in tracks");
+      SetCounterName(ECounter::RecoTofHit, "TOF hits in tracks");
       SetCounterName(ECounter::UndefinedMvdHit, "undefined MVD hits");
       SetCounterName(ECounter::UndefinedStsHit, "undefined STS hits");
       SetCounterName(ECounter::UndefinedMuchHit, "undefined MuCh hits");
diff --git a/algo/ca/core/utils/CaVector.h b/algo/ca/core/utils/CaVector.h
index 5630dfbdf46973e9dd39b020e86f5bb5a9d566a1..fdb5e1e2932a1ed73510653d39b0cde01360e178 100644
--- a/algo/ca/core/utils/CaVector.h
+++ b/algo/ca/core/utils/CaVector.h
@@ -265,6 +265,7 @@ namespace cbm::algo::ca
     using Tbase::pop_back;
     using Tbase::rbegin;
     using Tbase::reserve;
+    using Tbase::shrink_to_fit;
     using Tbase::size;
     using typename Tbase::iterator;
 
diff --git a/algo/detectors/tof/Hit.h b/algo/detectors/tof/Hit.h
index 23899e9a5d08fee05d13b35d0d949b2573a06914..e32939886f8e7c8d4df0ad12816899622ce8c223 100644
--- a/algo/detectors/tof/Hit.h
+++ b/algo/detectors/tof/Hit.h
@@ -3,6 +3,9 @@
    Authors: Dominik Smith [committer], Pierre-Alain Loizeau, Felix Weiglhofer */
 #pragma once
 
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/split_member.hpp>
+
 #include <cstdint>
 #include <vector>
 
@@ -11,9 +14,34 @@
 #include "Math/Rotation3D.h"
 #include "Math/Vector3Dfwd.h"
 
-namespace cbm::algo::tof
+namespace boost::serialization
 {
+  template<class Archive>
+  void save(Archive& ar, const ROOT::Math::XYZVector& val, const unsigned int /*version*/)
+  {
+    ar << val.X();
+    ar << val.Y();
+    ar << val.Z();
+  }
+
+  template<class Archive>
+  void load(Archive& ar, ROOT::Math::XYZVector& val, const unsigned int /*version*/)
+  {
+    double x, y, z;
+    ar >> x >> y >> z;
+    val.SetXYZ(x, y, z);
+  }
+
+  template<class Archive>
+  void serialize(Archive& ar, ROOT::Math::XYZVector& val, const unsigned int version)
+  {
+    split_free(ar, val, version);
+  }
+}  // namespace boost::serialization
 
+
+namespace cbm::algo::tof
+{
   struct Hit {
     ROOT::Math::XYZVector hitPos    = ROOT::Math::XYZVector(0.0, 0.0, 0.0);
     ROOT::Math::XYZVector hitPosErr = ROOT::Math::XYZVector(0.0, 0.0, 0.0);
@@ -100,6 +128,18 @@ namespace cbm::algo::tof
       // and real system time resolution
       hitPosErr = ROOT::Math::XYZVector(0.5, 0.5, 0.5);
     }
+
+    template<class Archive>
+    void serialize(Archive& ar, unsigned int /*version*/)
+    {
+      ar& hitPos;
+      ar& hitPosErr;
+      ar& hitTime;
+      ar& hitTimeErr;
+      ar& address;
+      ar& vDigiIndRef;
+      ar& weightsSum;
+    }
   };
 
 }  // namespace cbm::algo::tof
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 796ce31e1832dc386dc7af80883882c9a60025ca..4b03acfae3195ee230342797ea5b38b72abedeee 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -175,13 +175,18 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
         .stsHits = stsHits,
         .tofHits = tofHits,
       };
-      TrackingChain::Return_t trackingOutput = fTracking.Run(input);
-      if (Opts().HasOutput(RecoData::Track)) { results.tracks = std::move(trackingOutput.first); }
-      QueueTrackingMetrics(trackingOutput.second);
+      TrackingChain::Output_t trackingOutput = fTracking.Run(input);
+      if (Opts().HasOutput(RecoData::Track)) {
+        results.tracks             = std::move(trackingOutput.tracks);
+        results.trackStsHitIndices = std::move(trackingOutput.stsHitIndices);
+        results.trackTofHitIndices = std::move(trackingOutput.tofHitIndices);
+      }
+      QueueTrackingMetrics(trackingOutput.monitorData);
     }
 
     if (Opts().HasOutput(RecoData::DigiEvent)) results.events = std::move(events);
     if (Opts().HasOutput(RecoData::Hit)) results.stsHits = std::move(stsHits);
+    if (Opts().HasOutput(RecoData::Hit)) results.tofHits = std::move(tofHits);
   }
   PrintTimings(tsTimes);
 
@@ -371,7 +376,7 @@ void Reco::QueueTrackingMetrics(const ca::TrackingMonitorData& monitor)
                             {"caNofRecoTracks", monitor.GetCounterValue(ca::ECounter::RecoTrack)},
                             {"caNofRecoHitsTotal", monitor.GetCounterValue(ca::ECounter::RecoHit)},
                             {"caNofRecoHitsUsed", monitor.GetCounterValue(ca::ECounter::RecoHitUsed)},
-                            {"caNofWindowa", monitor.GetCounterValue(ca::ECounter::SubTS)}});
+                            {"caNofWindows", monitor.GetCounterValue(ca::ECounter::SubTS)}});
 }
 
 void Reco::QueueProcessingMetrics(const ProcessingMonitor& mon)
diff --git a/algo/global/RecoResults.h b/algo/global/RecoResults.h
index a8ed4e5d741d67623e8c3d7ee82429729d319291..42f1aa04a02d2b4fd69ef764b7d5369b80083668 100644
--- a/algo/global/RecoResults.h
+++ b/algo/global/RecoResults.h
@@ -13,6 +13,7 @@
 
 #include "DigiData.h"
 #include "PartitionedSpan.h"
+#include "PartitionedVector.h"
 #include "ca/core/utils/CaVector.h"
 #include "data/sts/Hit.h"
 
@@ -23,6 +24,9 @@ namespace cbm::algo
   struct RecoResults {
     std::vector<DigiEvent> events;
     PartitionedSpan<sts::Hit> stsHits;
+    PartitionedVector<tof::Hit> tofHits;
     ca::Vector<ca::Track> tracks;
+    ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>> trackStsHitIndices;
+    ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>> trackTofHitIndices;
   };
 }  // namespace cbm::algo
diff --git a/algo/global/StorableRecoResults.h b/algo/global/StorableRecoResults.h
index bc1f089d88ef670b33cf87b9070d351d54d2c274..dcf4d80ddefb58f8f832dd90c68521c161ca478e 100644
--- a/algo/global/StorableRecoResults.h
+++ b/algo/global/StorableRecoResults.h
@@ -12,6 +12,8 @@
 #include <cstdint>
 
 #include "PartitionedVector.h"
+#include "ca/core/data/CaTrack.h"
+#include "ca/core/utils/CaVector.h"
 
 namespace cbm::algo
 {
@@ -19,6 +21,8 @@ namespace cbm::algo
   class StorableRecoResults {
 
   public:
+    using TrackHitIndexContainer_t = ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>>;
+
     /**
      * @brief Default constructor (required by boost::serialization)
      */
@@ -42,12 +46,36 @@ namespace cbm::algo
     PartitionedVector<sts::Hit>& StsHits() { return fStsHits; }
     const PartitionedVector<sts::Hit>& StsHits() const { return fStsHits; }
 
+    PartitionedVector<tof::Hit>& TofHits() { return fTofHits; }
+    const PartitionedVector<tof::Hit>& TofHits() const { return fTofHits; }
+
+    ca::Vector<ca::Track>& Tracks() { return fTracks; }
+    const ca::Vector<ca::Track>& Tracks() const { return fTracks; }
+
+    TrackHitIndexContainer_t& TrackStsHitIndices() { return fTrackStsHitIndices; }
+    const TrackHitIndexContainer_t& TrackStsHitIndices() const { return fTrackStsHitIndices; }
+
+    TrackHitIndexContainer_t& TrackTofHitIndices() { return fTrackTofHitIndices; }
+    const TrackHitIndexContainer_t& TrackTofHitIndices() const { return fTrackTofHitIndices; }
+
   private:
     uint64_t fTsIndex     = UINT64_MAX;
     uint64_t fTsStartTime = UINT64_MAX;
 
     std::vector<CbmDigiEvent> fDigiEvents;
     PartitionedVector<sts::Hit> fStsHits;
+    PartitionedVector<tof::Hit> fTofHits;
+
+    // Tracking output
+    ca::Vector<ca::Track> fTracks;
+
+    /// \brief STS hit indices of tracks
+    /// \note  index: [trkID][hitID], value: pair(partitionID, hitPartitionID)
+    TrackHitIndexContainer_t fTrackStsHitIndices;
+
+    /// \brief TOF hit indices of tracks
+    /// \note  index: [trkID][hitID], value: pair(partitionID, hitPartitionID)
+    TrackHitIndexContainer_t fTrackTofHitIndices;
 
     friend class boost::serialization::access;
 
@@ -59,6 +87,10 @@ namespace cbm::algo
 
       ar& fDigiEvents;
       ar& fStsHits;
+      ar& fTofHits;
+      ar& fTracks;
+      ar& fTrackStsHitIndices;
+      ar& fTrackTofHitIndices;
     }
   };
 
diff --git a/reco/app/cbmreco/main.cxx b/reco/app/cbmreco/main.cxx
index 4e9fb1e9564472369e0395d836f75f56a87c82de..054846d55963fa72463dfad9a3b5b943217cbc5a 100644
--- a/reco/app/cbmreco/main.cxx
+++ b/reco/app/cbmreco/main.cxx
@@ -29,7 +29,11 @@ std::shared_ptr<StorableRecoResults> makeStorableRecoResults(const fles::Timesli
     storable->DigiEvents().emplace_back(digiEvent.ToStorable());
   }
 
-  storable->StsHits() = results.stsHits;
+  storable->StsHits()            = results.stsHits;
+  storable->TofHits()            = results.tofHits;
+  storable->Tracks()             = results.tracks;
+  storable->TrackStsHitIndices() = results.trackStsHitIndices;
+  storable->TrackTofHitIndices() = results.trackTofHitIndices;
 
   return storable;
 }
@@ -81,7 +85,6 @@ bool dumpArchive(const Options& opts)
       }
     }
 
-
     if (nEvents > DumpEventsPerTS) L_(info) << "...";
   }