diff --git a/Event/LumiEvent/CMakeLists.txt b/Event/LumiEvent/CMakeLists.txt
index d2259cdd377351c0e84a948ffd28b1e50c4bd87f..c2317d50099f747473e34b4b5a69073b85257953 100644
--- a/Event/LumiEvent/CMakeLists.txt
+++ b/Event/LumiEvent/CMakeLists.txt
@@ -18,15 +18,6 @@ gaudi_add_header_only_library(LumiEventLib
         Gaudi::GaudiKernel
 )
 
-gaudi_add_module(LumiEvent
-    SOURCES
-        src/HltLumiSummaryDecoder.cpp
-    LINK
-        LHCb::LHCbAlgsLib
-        LHCb::DAQEventLib
-        LHCb::LumiEventLib
-)
-
 gaudi_add_dictionary(LumiEventDict
     HEADERFILES dict/dictionary.h
     SELECTION dict/selection.xml
diff --git a/Event/LumiEvent/include/Event/HltLumiSummary.h b/Event/LumiEvent/include/Event/HltLumiSummary.h
index c7832c410e99123bd218285ded8184b383955667..4e60776eea45fbf92cce868e79d30fe4d69cf568 100644
--- a/Event/LumiEvent/include/Event/HltLumiSummary.h
+++ b/Event/LumiEvent/include/Event/HltLumiSummary.h
@@ -18,7 +18,7 @@ namespace LHCb {
   // Forward declarations
 
   // Class ID definition
-  static const CLID CLID_HltLumiSummary = 7520;
+  static const CLID CLID_HltLumiSummary = 7540;
 
   // Namespace for locations in TDS
   namespace HltLumiSummaryLocation {
@@ -36,7 +36,7 @@ namespace LHCb {
   class HltLumiSummary final : public DataObject {
   public:
     /// User information
-    typedef GaudiUtils::VectorMap<int, int> ExtraInfo;
+    typedef GaudiUtils::VectorMap<std::string, int> ExtraInfo;
 
     /// Default Constructor
     HltLumiSummary() = default;
@@ -55,21 +55,21 @@ namespace LHCb {
     const ExtraInfo& extraInfo() const { return m_extraInfo; }
 
     /// has information for specified key
-    bool hasInfo( int key ) const { return m_extraInfo.end() != m_extraInfo.find( key ); }
+    bool hasInfo( std::string const& key ) const { return m_extraInfo.end() != m_extraInfo.find( key ); }
 
     ///  Add new information associated with the specified key. This method cannot be used to modify information for a
     ///  pre-existing key.
-    bool addInfo( int key, int info ) { return m_extraInfo.insert( key, info ).second; }
+    bool addInfo( std::string const& key, int info ) { return m_extraInfo.insert( key, info ).second; }
 
     /// extract the information associated with the given key. If there is no such infomration the default value will be
     /// returned.
-    int info( const int key, const int def ) const {
+    int info( const std::string& key, const int def ) const {
       auto i = m_extraInfo.find( key );
       return m_extraInfo.end() == i ? def : i->second;
     }
 
     /// erase the information associated with the given key
-    unsigned long eraseInfo( int key ) { return m_extraInfo.erase( key ); }
+    unsigned long eraseInfo( std::string const& key ) { return m_extraInfo.erase( key ); }
 
     /// Update  Some addtional user information. Don't use directly. Use *Info() methods.
     HltLumiSummary& setExtraInfo( ExtraInfo value ) {
diff --git a/Event/LumiEvent/include/Event/LumiCounters.h b/Event/LumiEvent/include/Event/LumiCounters.h
deleted file mode 100644
index fc775ae0880145de71e123792a1130f101a0c559..0000000000000000000000000000000000000000
--- a/Event/LumiEvent/include/Event/LumiCounters.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/*****************************************************************************\
-* (c) Copyright 2000-2019 CERN for the benefit of the LHCb Collaboration      *
-*                                                                             *
-* This software is distributed under the terms of the GNU General Public      *
-* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
-*                                                                             *
-* In applying this licence, CERN does not waive the privileges and immunities *
-* granted to it by virtue of its status as an Intergovernmental Organization  *
-* or submit itself to any jurisdiction.                                       *
-\*****************************************************************************/
-#pragma once
-#include "GaudiKernel/DataObject.h"
-#include "GaudiKernel/VectorMap.h"
-#include <algorithm>
-#include <ostream>
-
-namespace LHCb {
-
-  // Forward declarations
-
-  // Class ID definition
-  static const CLID CLID_LumiCounters = 13500;
-
-  /** @class LumiCounters LumiCounters.h
-   *
-   * Enum class for Lumi counters
-   *
-   * @author Jaap Panman
-   *
-   */
-
-  class LumiCounters final {
-  public:
-    /// lookup table for counter keys
-    enum counterKey {
-      PUMult          = 0,  // number of PU hits
-      RZVelo          = 1,  // number of Velo tracks
-      RZVeloBW        = 2,  // number of backward RZVelo tracks
-      Velo            = 3,  // number of Velo tracks pointing to IR
-      Muon            = 4,  // number of muon tracks
-      BKMuon          = 5,  // number of muon tracks NOT pointing to PV
-      SPDMult         = 6,  // number of SPD hits
-      CaloEt          = 7,  // Calo Et
-      TTIP            = 8,  // number of TT tracks from the IP
-      TTMIB           = 9,  // number of TT tracks parallel with beam
-      PV2D            = 10, // number of 2D vertices
-      PV3D            = 11, // number of 3D vertices
-      PU              = 13, // number of PU vertices
-      Vertex          = 14, // number of vertices accepted in IR
-      Method          = 20, // method: 0 or missing is random method
-      Random          = 21, // random method: 0 or missing is L0 method
-      PoissonPUMult   = 50, // number of times PU hits over threshold
-      PoissonRZVelo   = 51, // number of times Velo tracks over threshold
-      PoissonRZVeloBW = 52, // number of times backward RZVelo tracks over threshold
-      PoissonVelo     = 53, // number of times Velo tracks pointing to IR over threshold
-      PoissonMuon     = 54, // number of times muon tracks over threshold
-      PoissonBKMuon   = 55, // number of times muon tracks NOT pointing to PV over threshold
-      PoissonSPDMult  = 56, // number of times SPD hits over threshold
-      PoissonCaloEt   = 57, // number of times Calo Et over threshold
-      PoissonTTIP     = 58, // number of timesTT tracks from the IP over threshold
-      PoissonTTMIB    = 59, // number of timesTT tracks parallel with beam over threshold
-      PoissonPV2D     = 60, // number of times2D vertices over threshold
-      PoissonPV3D     = 61, // number of times 3D vertices over threshold
-      PoissonPU       = 63, // number of times PU vertices over threshold
-      PoissonVertex   = 64, // number of times vertices in IR range over threshold
-      PoissonMethod   = 70, // method: 0 or missing is random method
-      PoissonRandom   = 71, // random method: 0 or missing is L0 method
-      Unknown         = 98, // unknown value
-      LastGlobal      = 99  // User variables should use flags greater than this value
-    };
-
-    LumiCounters() = delete; // class contains no data, so no use to instantiate it...
-
-    /// conversion of string to enum for type counterKey
-    static LHCb::LumiCounters::counterKey counterKeyToType( const std::string& aName );
-
-    /// conversion to string for enum type counterKey
-    static const std::string& counterKeyToString( int aEnum );
-
-  private:
-    static const GaudiUtils::VectorMap<std::string, counterKey>& s_counterKeyTypMap();
-
-  }; // class LumiCounters
-
-  inline std::ostream& operator<<( std::ostream& s, LHCb::LumiCounters::counterKey e ) {
-    switch ( e ) {
-    case LHCb::LumiCounters::PUMult:
-      return s << "PUMult";
-    case LHCb::LumiCounters::RZVelo:
-      return s << "RZVelo";
-    case LHCb::LumiCounters::RZVeloBW:
-      return s << "RZVeloBW";
-    case LHCb::LumiCounters::Velo:
-      return s << "Velo";
-    case LHCb::LumiCounters::Muon:
-      return s << "Muon";
-    case LHCb::LumiCounters::BKMuon:
-      return s << "BKMuon";
-    case LHCb::LumiCounters::SPDMult:
-      return s << "SPDMult";
-    case LHCb::LumiCounters::CaloEt:
-      return s << "CaloEt";
-    case LHCb::LumiCounters::TTIP:
-      return s << "TTIP";
-    case LHCb::LumiCounters::TTMIB:
-      return s << "TTMIB";
-    case LHCb::LumiCounters::PV2D:
-      return s << "PV2D";
-    case LHCb::LumiCounters::PV3D:
-      return s << "PV3D";
-    case LHCb::LumiCounters::PU:
-      return s << "PU";
-    case LHCb::LumiCounters::Vertex:
-      return s << "Vertex";
-    case LHCb::LumiCounters::Method:
-      return s << "Method";
-    case LHCb::LumiCounters::Random:
-      return s << "Random";
-    case LHCb::LumiCounters::PoissonPUMult:
-      return s << "PoissonPUMult";
-    case LHCb::LumiCounters::PoissonRZVelo:
-      return s << "PoissonRZVelo";
-    case LHCb::LumiCounters::PoissonRZVeloBW:
-      return s << "PoissonRZVeloBW";
-    case LHCb::LumiCounters::PoissonVelo:
-      return s << "PoissonVelo";
-    case LHCb::LumiCounters::PoissonMuon:
-      return s << "PoissonMuon";
-    case LHCb::LumiCounters::PoissonBKMuon:
-      return s << "PoissonBKMuon";
-    case LHCb::LumiCounters::PoissonSPDMult:
-      return s << "PoissonSPDMult";
-    case LHCb::LumiCounters::PoissonCaloEt:
-      return s << "PoissonCaloEt";
-    case LHCb::LumiCounters::PoissonTTIP:
-      return s << "PoissonTTIP";
-    case LHCb::LumiCounters::PoissonTTMIB:
-      return s << "PoissonTTMIB";
-    case LHCb::LumiCounters::PoissonPV2D:
-      return s << "PoissonPV2D";
-    case LHCb::LumiCounters::PoissonPV3D:
-      return s << "PoissonPV3D";
-    case LHCb::LumiCounters::PoissonPU:
-      return s << "PoissonPU";
-    case LHCb::LumiCounters::PoissonVertex:
-      return s << "PoissonVertex";
-    case LHCb::LumiCounters::PoissonMethod:
-      return s << "PoissonMethod";
-    case LHCb::LumiCounters::PoissonRandom:
-      return s << "PoissonRandom";
-    case LHCb::LumiCounters::Unknown:
-      return s << "Unknown";
-    case LHCb::LumiCounters::LastGlobal:
-      return s << "LastGlobal";
-    default:
-      return s << "ERROR wrong value " << int( e ) << " for enum LHCb::LumiCounters::counterKey";
-    }
-  }
-
-} // namespace LHCb
-
-// -----------------------------------------------------------------------------
-// end of class
-// -----------------------------------------------------------------------------
-
-// Including forward declarations
-
-inline const GaudiUtils::VectorMap<std::string, LHCb::LumiCounters::counterKey>&
-LHCb::LumiCounters::s_counterKeyTypMap() {
-  static const GaudiUtils::VectorMap<std::string, counterKey> m = {{"PUMult", PUMult},
-                                                                   {"RZVelo", RZVelo},
-                                                                   {"RZVeloBW", RZVeloBW},
-                                                                   {"Velo", Velo},
-                                                                   {"Muon", Muon},
-                                                                   {"BKMuon", BKMuon},
-                                                                   {"SPDMult", SPDMult},
-                                                                   {"CaloEt", CaloEt},
-                                                                   {"TTIP", TTIP},
-                                                                   {"TTMIB", TTMIB},
-                                                                   {"PV2D", PV2D},
-                                                                   {"PV3D", PV3D},
-                                                                   {"PU", PU},
-                                                                   {"Vertex", Vertex},
-                                                                   {"Method", Method},
-                                                                   {"Random", Random},
-                                                                   {"PoissonPUMult", PoissonPUMult},
-                                                                   {"PoissonRZVelo", PoissonRZVelo},
-                                                                   {"PoissonRZVeloBW", PoissonRZVeloBW},
-                                                                   {"PoissonVelo", PoissonVelo},
-                                                                   {"PoissonMuon", PoissonMuon},
-                                                                   {"PoissonBKMuon", PoissonBKMuon},
-                                                                   {"PoissonSPDMult", PoissonSPDMult},
-                                                                   {"PoissonCaloEt", PoissonCaloEt},
-                                                                   {"PoissonTTIP", PoissonTTIP},
-                                                                   {"PoissonTTMIB", PoissonTTMIB},
-                                                                   {"PoissonPV2D", PoissonPV2D},
-                                                                   {"PoissonPV3D", PoissonPV3D},
-                                                                   {"PoissonPU", PoissonPU},
-                                                                   {"PoissonVertex", PoissonVertex},
-                                                                   {"PoissonMethod", PoissonMethod},
-                                                                   {"PoissonRandom", PoissonRandom},
-                                                                   {"Unknown", Unknown},
-                                                                   {"LastGlobal", LastGlobal}};
-  return m;
-}
-
-inline LHCb::LumiCounters::counterKey LHCb::LumiCounters::counterKeyToType( const std::string& aName ) {
-  auto iter = s_counterKeyTypMap().find( aName );
-  return iter != s_counterKeyTypMap().end() ? iter->second : Unknown;
-}
-
-inline const std::string& LHCb::LumiCounters::counterKeyToString( int aEnum ) {
-  static const std::string s_Unknown = "Unknown";
-  auto                     iter      = std::find_if( s_counterKeyTypMap().begin(), s_counterKeyTypMap().end(),
-                            [&]( const std::pair<const std::string, counterKey>& i ) { return i.second == aEnum; } );
-  return iter != s_counterKeyTypMap().end() ? iter->first : s_Unknown;
-}
diff --git a/Event/LumiEvent/src/HltLumiSummaryDecoder.cpp b/Event/LumiEvent/src/HltLumiSummaryDecoder.cpp
deleted file mode 100644
index ba2f8355a07242abfc0feff765795d5f83ee0d47..0000000000000000000000000000000000000000
--- a/Event/LumiEvent/src/HltLumiSummaryDecoder.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*****************************************************************************\
-* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
-*                                                                             *
-* This software is distributed under the terms of the GNU General Public      *
-* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
-*                                                                             *
-* In applying this licence, CERN does not waive the privileges and immunities *
-* granted to it by virtue of its status as an Intergovernmental Organization  *
-* or submit itself to any jurisdiction.                                       *
-\*****************************************************************************/
-
-#include "Event/HltLumiSummary.h"
-#include "Event/RawBank.h"
-#include "Event/RawEvent.h"
-
-#include "LHCbAlgs/Transformer.h"
-
-#include "Gaudi/Accumulators.h"
-
-#include <algorithm>
-#include <string>
-
-namespace LHCb {
-
-  /**
-   *  Decodes the LumiSummary.
-   *
-   *  @author Jaap Panman
-   *  HenryIII Changed to use Transform Algorithm
-   *
-   *  @date   2008-08-01
-   */
-
-  class HltLumiSummaryDecoder : public Algorithm::Transformer<HltLumiSummary( const RawEvent& )> {
-  public:
-    /// Standard constructor
-    HltLumiSummaryDecoder( const std::string& name, ISvcLocator* pSvcLocator )
-        : Transformer( name, pSvcLocator,
-                       KeyValue{"RawEventLocations", Gaudi::Functional::concat_alternatives(
-                                                         RawEventLocation::Trigger, RawEventLocation::Default )},
-                       KeyValue{"OutputContainerName", HltLumiSummaryLocation::Default} ) {}
-
-    HltLumiSummary operator()( const RawEvent& event ) const override {
-
-      HltLumiSummary hltLumiSummary;
-
-      // Get the buffers associated with the HltLumiSummary
-      // Now copy the information from all banks (normally there should only be one)
-      auto size_buffer = m_totDataSize.buffer();
-      for ( const auto& ibank : event.banks( RawBank::HltLumiSummary ) ) {
-        // get the raw data
-        for ( const unsigned w : ibank->range<unsigned int>() ) {
-          // decode the info
-          int iKey = ( w >> 16 );
-          int iVal = ( w & 0xFFFF );
-          if ( msgLevel( MSG::VERBOSE ) ) { verbose() << format( " %8x %11d %11d %11d ", w, w, iKey, iVal ) << endmsg; }
-          // add this counter
-          hltLumiSummary.addInfo( iKey, iVal );
-        }
-
-        // keep statistics
-        int totDataSize = ibank->size() / sizeof( unsigned int );
-        size_buffer += totDataSize;
-
-        if ( msgLevel( MSG::DEBUG ) ) {
-          debug() << "Bank size: " << format( "%4d ", ibank->size() ) << "Total Data bank size " << totDataSize
-                  << endmsg;
-        }
-      }
-
-      return hltLumiSummary;
-    }
-
-  private:
-    // Statistics, mutable to allow statistics to be kept
-    mutable Gaudi::Accumulators::AveragingCounter<> m_totDataSize{this, "Average event size / 32-bit words"};
-  };
-
-} // namespace LHCb
-
-DECLARE_COMPONENT_WITH_ID( LHCb::HltLumiSummaryDecoder, "HltLumiSummaryDecoder" )
diff --git a/Event/RecEvent/dict/selection.xml b/Event/RecEvent/dict/selection.xml
index ced07a942d889d7a0eaaeb4001d852ddc18df7ee..f0054e51cebeafb0d24d05b27f93921d6973b800 100755
--- a/Event/RecEvent/dict/selection.xml
+++ b/Event/RecEvent/dict/selection.xml
@@ -40,6 +40,7 @@
   <class name="LHCb::ProtoParticle" id="00000323-0000-0000-0000-000000000000"/>
   <class name="LHCb::RecHeader" id="00000069-0000-0000-0000-000000000000"/>
   <class name="LHCb::RecSummary" id="0000006a-0000-0000-0000-000000000000"/>
+  <class name="LHCb::RecSummary::SummaryData"/>
   <class name="LHCb::RecVertex" id="0000272e-0000-0000-0000-000000000000"/>
   <class name="LHCb::RichPID" id="00003072-0000-0000-0000-000000000000"/>
   <class name="LHCb::RichSummaryPhoton"/>
diff --git a/Hlt/HltDAQ/CMakeLists.txt b/Hlt/HltDAQ/CMakeLists.txt
index 77195f96a7b02dc14255b453e4f8fc6e06be29e5..f00be24abdd14e0b5a9a6780abceb8df961b7e07 100644
--- a/Hlt/HltDAQ/CMakeLists.txt
+++ b/Hlt/HltDAQ/CMakeLists.txt
@@ -20,10 +20,12 @@ gaudi_add_module(HltDAQ
         src/component/HltDecoderUtils.cpp
         src/component/HltDecReportsWriter.cpp
         src/component/HltDiffHltDecReports.cpp
+        src/component/HltLumiSummaryDecoder.cpp
+        src/component/HltLumiSummaryMonitor.cpp
         src/component/HltLumiWriter.cpp
         src/component/HltPackedBufferWriter.cpp
-        src/component/HltPackedBufferDecoder.cpp 
-        src/component/HltPackedBufferChecker.cpp 
+        src/component/HltPackedBufferDecoder.cpp
+        src/component/HltPackedBufferChecker.cpp
         src/component/HltRawDataMonitor.cpp
         src/component/HltRoutingBitsFilter.cpp
         src/component/HltSelReportsDecoder.cpp
diff --git a/Hlt/HltDAQ/src/component/HltLumiSummaryDecoder.cpp b/Hlt/HltDAQ/src/component/HltLumiSummaryDecoder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..80eba740d14e88e995021c76a6bebd617057fc3f
--- /dev/null
+++ b/Hlt/HltDAQ/src/component/HltLumiSummaryDecoder.cpp
@@ -0,0 +1,254 @@
+/*****************************************************************************\
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+
+#include "Event/HltLumiSummary.h"
+#include "Event/LumiSummaryOffsets_V1.h"
+#include "Event/LumiSummaryOffsets_V2.h"
+#include "Event/RawBank.h"
+#include "Event/RawEvent.h"
+#include "Kernel/IIndexedLumiSchemaSvc.h"
+
+#include "LHCbAlgs/Transformer.h"
+
+#include "Gaudi/Accumulators.h"
+
+#include <algorithm>
+#include <string>
+
+namespace LHCb {
+
+  /**
+   *  Decodes the LumiSummary.
+   *
+   *  @author Jaap Panman
+   *  HenryIII Changed to use Transform Algorithm
+   *
+   *  @date   2008-08-01
+   */
+
+  class HltLumiSummaryDecoder : public Algorithm::Transformer<HltLumiSummary( const RawEvent& )> {
+  public:
+    /// Standard constructor
+    HltLumiSummaryDecoder( const std::string& name, ISvcLocator* pSvcLocator )
+        : Transformer( name, pSvcLocator,
+                       KeyValue{"RawEventLocations", Gaudi::Functional::concat_alternatives(
+                                                         RawEventLocation::Trigger, RawEventLocation::Default )},
+                       KeyValue{"OutputContainerName", HltLumiSummaryLocation::Default} ) {}
+
+    unsigned getField( unsigned offset, unsigned size, const unsigned* target ) const {
+      // Separate offset into a word part and bit part
+      unsigned word      = offset / ( 8 * sizeof( unsigned ) );
+      unsigned bitoffset = offset % ( 8 * sizeof( unsigned ) );
+
+      // Check size and offset line up with word boundaries
+      if ( bitoffset + size > ( 8 * sizeof( unsigned ) ) ) { return 0; }
+
+      unsigned mask = ( ( 1ul << size ) - 1 );
+      return ( ( target[word] >> bitoffset ) & mask );
+    }
+
+    HltLumiSummary operator()( const RawEvent& event ) const override {
+
+      HltLumiSummary hltLumiSummary;
+
+      // Get the buffers associated with the HltLumiSummary
+      // Now copy the information from all banks (normally there should only be one)
+      auto size_buffer      = m_totDataSize.buffer();
+      auto lumiSummaryBanks = event.banks( RawBank::HltLumiSummary );
+      if ( lumiSummaryBanks.empty() ) {
+        throw GaudiException( "No LumiSummary RawBank in RawEvent. Did you filter on decision or routing bit?", name(),
+                              StatusCode::FAILURE );
+      }
+      for ( const auto& ibank : lumiSummaryBanks ) {
+        if ( ibank->version() == 0 ) {
+          // legacy Run 1+2 LumiSummary structure
+          warning() << "Unsupported HltLumiSummary version: " << format( "%4d", ibank->version() ) << endmsg;
+        } else if ( ibank->version() == 1 ) {
+          // initial Run 3 version
+          if ( ibank->size() != LHCb::LumiSummaryOffsets::V1::TotalSize / 8 ) {
+            warning() << "Bank size incorrect for HltLumiSummary V1: expected "
+                      << format( "%4d", LHCb::LumiSummaryOffsets::V1::TotalSize ) << ", found "
+                      << format( "%4d", ibank->size() ) << endmsg;
+          } else {
+            hltLumiSummary.addInfo( "T0Low", getField( LHCb::LumiSummaryOffsets::V1::t0LowOffset,
+                                                       LHCb::LumiSummaryOffsets::V1::t0LowSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "T0High", getField( LHCb::LumiSummaryOffsets::V1::t0HighOffset,
+                                                        LHCb::LumiSummaryOffsets::V1::t0HighSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "BCIDLow", getField( LHCb::LumiSummaryOffsets::V1::bcidLowOffset,
+                                                         LHCb::LumiSummaryOffsets::V1::bcidLowSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "BCIDHigh", getField( LHCb::LumiSummaryOffsets::V1::bcidHighOffset,
+                                                          LHCb::LumiSummaryOffsets::V1::bcidHighSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "BXType", getField( LHCb::LumiSummaryOffsets::V1::bxTypeOffset,
+                                                        LHCb::LumiSummaryOffsets::V1::bxTypeSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "GEC", getField( LHCb::LumiSummaryOffsets::V1::GecOffset,
+                                                     LHCb::LumiSummaryOffsets::V1::GecSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "VeloTracks",
+                                    getField( LHCb::LumiSummaryOffsets::V1::VeloTracksOffset,
+                                              LHCb::LumiSummaryOffsets::V1::VeloTracksSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "VeloVertices",
+                                    getField( LHCb::LumiSummaryOffsets::V1::VeloVerticesOffset,
+                                              LHCb::LumiSummaryOffsets::V1::VeloVerticesSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "SciFiClusters",
+                                    getField( LHCb::LumiSummaryOffsets::V1::SciFiClustersOffset,
+                                              LHCb::LumiSummaryOffsets::V1::SciFiClustersSize, ibank->data() ) );
+            hltLumiSummary.addInfo( "MuonHitsM2R2", getField( LHCb::LumiSummaryOffsets::V1::M2R2Offset,
+                                                              LHCb::LumiSummaryOffsets::V1::M2R2Size, ibank->data() ) );
+            hltLumiSummary.addInfo( "MuonHitsM2R3", getField( LHCb::LumiSummaryOffsets::V1::M2R3Offset,
+                                                              LHCb::LumiSummaryOffsets::V1::M2R3Size, ibank->data() ) );
+            hltLumiSummary.addInfo( "MuonHitsM3R2", getField( LHCb::LumiSummaryOffsets::V1::M3R2Offset,
+                                                              LHCb::LumiSummaryOffsets::V1::M3R2Size, ibank->data() ) );
+            hltLumiSummary.addInfo( "MuonHitsM3R3", getField( LHCb::LumiSummaryOffsets::V1::M3R3Offset,
+                                                              LHCb::LumiSummaryOffsets::V1::M3R3Size, ibank->data() ) );
+          }
+        } else if ( ibank->version() == 2 ) {
+          auto encodingKey = getField( LHCb::LumiSummaryOffsets::V2::encodingKeyOffset,
+                                       LHCb::LumiSummaryOffsets::V2::encodingKeySize, ibank->data() );
+          if ( msgLevel( MSG::VERBOSE ) ) { verbose() << "encodingKey :" << encodingKey << endmsg; }
+          if ( encodingKey != 0x61967914 ) { // This encoding key is the hash of the JSON representation of the initial
+                                             // fixed V2 schema
+            auto lumi_schema = m_svc->lumiCounters( encodingKey, 0 );
+            if ( static_cast<unsigned>( ibank->size() ) != lumi_schema.size ) {
+              warning() << "Bank size incorrect for HltLumiSummary V2: expected " << format( "%4d", lumi_schema.size )
+                        << " for encoding key " << format( "%8x", encodingKey ) << ", found "
+                        << format( "%4d", ibank->size() ) << endmsg;
+            } else {
+              for ( auto cntr : lumi_schema.counters ) {
+                hltLumiSummary.addInfo( cntr.name, getField( cntr.offset, cntr.size, ibank->data() ) );
+                if ( msgLevel( MSG::VERBOSE ) ) {
+                  verbose() << cntr.name << " (" << cntr.offset << "," << cntr.size
+                            << ") :" << getField( cntr.offset, cntr.size, ibank->data() ) << endmsg;
+                }
+              }
+            }
+          } else {
+            if ( ibank->size() != LHCb::LumiSummaryOffsets::V2::TotalSize / 8 ) {
+              warning() << "Bank size incorrect for HltLumiSummary V2: expected "
+                        << format( "%4d", LHCb::LumiSummaryOffsets::V2::TotalSize ) << ", found "
+                        << format( "%4d", ibank->size() ) << endmsg;
+            } else {
+              hltLumiSummary.addInfo( "encodingKey",
+                                      getField( LHCb::LumiSummaryOffsets::V2::encodingKeyOffset,
+                                                LHCb::LumiSummaryOffsets::V2::encodingKeySize, ibank->data() ) );
+              hltLumiSummary.addInfo( "T0Low", getField( LHCb::LumiSummaryOffsets::V2::T0LowOffset,
+                                                         LHCb::LumiSummaryOffsets::V2::T0LowSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "T0High", getField( LHCb::LumiSummaryOffsets::V2::T0HighOffset,
+                                                          LHCb::LumiSummaryOffsets::V2::T0HighSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "BCIDLow", getField( LHCb::LumiSummaryOffsets::V2::BCIDLowOffset,
+                                                           LHCb::LumiSummaryOffsets::V2::BCIDLowSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "BCIDHigh",
+                                      getField( LHCb::LumiSummaryOffsets::V2::BCIDHighOffset,
+                                                LHCb::LumiSummaryOffsets::V2::BCIDHighSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "BXType", getField( LHCb::LumiSummaryOffsets::V2::BXTypeOffset,
+                                                          LHCb::LumiSummaryOffsets::V2::BXTypeSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "GEC", getField( LHCb::LumiSummaryOffsets::V2::GECOffset,
+                                                       LHCb::LumiSummaryOffsets::V2::GECSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "VeloTracks",
+                                      getField( LHCb::LumiSummaryOffsets::V2::VeloTracksOffset,
+                                                LHCb::LumiSummaryOffsets::V2::VeloTracksSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "VeloVertices",
+                                      getField( LHCb::LumiSummaryOffsets::V2::VeloVerticesOffset,
+                                                LHCb::LumiSummaryOffsets::V2::VeloVerticesSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "SciFiClusters",
+                                      getField( LHCb::LumiSummaryOffsets::V2::SciFiClustersOffset,
+                                                LHCb::LumiSummaryOffsets::V2::SciFiClustersSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "SciFiClustersS1M45",
+                                      getField( LHCb::LumiSummaryOffsets::V2::SciFiClustersS1M45Offset,
+                                                LHCb::LumiSummaryOffsets::V2::SciFiClustersS1M45Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "SciFiClustersS2M45",
+                                      getField( LHCb::LumiSummaryOffsets::V2::SciFiClustersS2M45Offset,
+                                                LHCb::LumiSummaryOffsets::V2::SciFiClustersS2M45Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "SciFiClustersS3M45",
+                                      getField( LHCb::LumiSummaryOffsets::V2::SciFiClustersS3M45Offset,
+                                                LHCb::LumiSummaryOffsets::V2::SciFiClustersS3M45Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "SciFiClustersS2M123",
+                                      getField( LHCb::LumiSummaryOffsets::V2::SciFiClustersS2M123Offset,
+                                                LHCb::LumiSummaryOffsets::V2::SciFiClustersS2M123Size,
+                                                ibank->data() ) );
+              hltLumiSummary.addInfo( "SciFiClustersS3M123",
+                                      getField( LHCb::LumiSummaryOffsets::V2::SciFiClustersS3M123Offset,
+                                                LHCb::LumiSummaryOffsets::V2::SciFiClustersS3M123Size,
+                                                ibank->data() ) );
+              hltLumiSummary.addInfo( "ECalET", getField( LHCb::LumiSummaryOffsets::V2::ECalETOffset,
+                                                          LHCb::LumiSummaryOffsets::V2::ECalETSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "ECalEInnerTop",
+                                      getField( LHCb::LumiSummaryOffsets::V2::ECalEInnerTopOffset,
+                                                LHCb::LumiSummaryOffsets::V2::ECalEInnerTopSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "ECalEInnerBottom",
+                                      getField( LHCb::LumiSummaryOffsets::V2::ECalEInnerBottomOffset,
+                                                LHCb::LumiSummaryOffsets::V2::ECalEInnerBottomSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "ECalEMiddleTop",
+                                      getField( LHCb::LumiSummaryOffsets::V2::ECalEMiddleTopOffset,
+                                                LHCb::LumiSummaryOffsets::V2::ECalEMiddleTopSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "ECalEMiddleBottom",
+                                      getField( LHCb::LumiSummaryOffsets::V2::ECalEMiddleBottomOffset,
+                                                LHCb::LumiSummaryOffsets::V2::ECalEMiddleBottomSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "ECalEOuterTop",
+                                      getField( LHCb::LumiSummaryOffsets::V2::ECalEOuterTopOffset,
+                                                LHCb::LumiSummaryOffsets::V2::ECalEOuterTopSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "ECalEOuterBottom",
+                                      getField( LHCb::LumiSummaryOffsets::V2::ECalEOuterBottomOffset,
+                                                LHCb::LumiSummaryOffsets::V2::ECalEOuterBottomSize, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM2R1",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM2R1Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM2R1Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM2R2",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM2R2Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM2R2Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM2R3",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM2R3Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM2R3Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM2R4",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM2R4Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM2R4Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM3R1",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM3R1Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM3R1Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM3R2",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM3R2Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM3R2Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM3R3",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM3R3Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM3R3Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM3R4",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM3R4Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM3R4Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM4R1",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM4R1Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM4R1Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM4R2",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM4R2Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM4R2Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM4R3",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM4R3Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM4R3Size, ibank->data() ) );
+              hltLumiSummary.addInfo( "MuonHitsM4R4",
+                                      getField( LHCb::LumiSummaryOffsets::V2::MuonHitsM4R4Offset,
+                                                LHCb::LumiSummaryOffsets::V2::MuonHitsM4R4Size, ibank->data() ) );
+            }
+          }
+        } else {
+          warning() << "Unknown HltLumiSummary version: " << format( "%4d", ibank->version() ) << endmsg;
+        }
+      }
+
+      return hltLumiSummary;
+    }
+
+  private:
+    ServiceHandle<IIndexedLumiSchemaSvc> m_svc{this, "DecoderMapping", "HltANNSvc"};
+
+    // Statistics, mutable to allow statistics to be kept
+    mutable Gaudi::Accumulators::AveragingCounter<> m_totDataSize{this, "Average event size / 32-bit words"};
+  };
+
+} // namespace LHCb
+
+DECLARE_COMPONENT_WITH_ID( LHCb::HltLumiSummaryDecoder, "HltLumiSummaryDecoder" )
diff --git a/Hlt/HltDAQ/src/component/HltLumiSummaryMonitor.cpp b/Hlt/HltDAQ/src/component/HltLumiSummaryMonitor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..69766c1c30be0bd3ea83bbf33fa1fe926c41da2d
--- /dev/null
+++ b/Hlt/HltDAQ/src/component/HltLumiSummaryMonitor.cpp
@@ -0,0 +1,92 @@
+/*****************************************************************************\
+* (c) Copyright 2019 CERN for the benefit of the LHCb Collaboration           *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+#include "Event/HltLumiSummary.h"
+#include "Event/ODIN.h"
+#include "LHCbAlgs/Consumer.h"
+
+#include "Gaudi/Accumulators/Histogram.h"
+#include "GaudiKernel/EventContext.h"
+#include "GaudiKernel/StatusCode.h"
+#include "Kernel/EventContextExt.h"
+#include "Kernel/IANNSvc.h"
+#include <string>
+#include <type_traits>
+#include <vector>
+
+using BXTypes = LHCb::ODIN::BXTypes;
+
+namespace {
+  template <typename T, typename K, typename OWNER>
+  void map_emplace( T& t, K&& key, OWNER* owner, std::string const& name, std::string const& title,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1 ) {
+    t.emplace( std::piecewise_construct, std::forward_as_tuple( key ),
+               std::forward_as_tuple( owner, name, title, axis1 ) );
+  }
+
+  template <typename T>
+  std::string to_string( const T& streamable ) {
+    std::ostringstream oss;
+    oss << streamable;
+    return oss.str();
+  }
+
+  const auto AxisBCID = Gaudi::Accumulators::Axis<double>( 3565, -0.5, 3564.5 );
+  const auto AxisTime = Gaudi::Accumulators::Axis<double>( 3600, 0, 3600 );
+} // namespace
+
+class HltLumiSummaryMonitor final
+    : public LHCb::Algorithm::Consumer<void( const LHCb::HltLumiSummary&, const LHCb::ODIN& )> {
+public:
+  HltLumiSummaryMonitor( const std::string& name, ISvcLocator* pSvcLocator )
+      : Consumer{name, pSvcLocator, {KeyValue{"Input", ""}, KeyValue{"ODIN", ""}}} {}
+  void operator()( const LHCb::HltLumiSummary&, const LHCb::ODIN& ) const override;
+
+private:
+  mutable std::map<std::pair<BXTypes, std::string>, Gaudi::Accumulators::Histogram<1>>        m_value;
+  mutable std::map<std::pair<BXTypes, std::string>, Gaudi::Accumulators::ProfileHistogram<1>> m_time_mean;
+  mutable std::map<std::pair<BXTypes, std::string>, Gaudi::Accumulators::ProfileHistogram<1>> m_time_pnz;
+
+  mutable std::mutex m_lock;
+};
+
+DECLARE_COMPONENT( HltLumiSummaryMonitor )
+
+void HltLumiSummaryMonitor::operator()( const LHCb::HltLumiSummary& summary, const LHCb::ODIN& odin ) const {
+  auto lock = std::scoped_lock{m_lock};
+
+  if ( m_value.empty() ) {
+    for ( auto bx : {BXTypes::NoBeam, BXTypes::Beam1, BXTypes::Beam2, BXTypes::BeamCrossing} ) {
+      for ( const auto& info : summary.extraInfo() ) {
+        const auto& counter = info.first;
+        map_emplace( m_value, std::make_pair( bx, counter ), this, "value_" + to_string( bx ) + "_" + counter,
+                     "Counter value for " + counter + "/" + to_string( bx ),
+                     Gaudi::Accumulators::Axis<double>( 1024, 0, 1024 ) );
+        map_emplace( m_time_mean, std::make_pair( bx, counter ), this, "mean_" + to_string( bx ) + "_" + counter,
+                     "Mean value per second for " + counter + "/" + to_string( bx ), AxisTime );
+        map_emplace( m_time_pnz, std::make_pair( bx, counter ), this, "pnz_" + to_string( bx ) + "_" + counter,
+                     "P(>0) per second for " + counter + "/" + to_string( bx ), AxisTime );
+      }
+    }
+  }
+
+  const auto time = ( odin.gpsTime() / 1000000 ) % 3600;
+  for ( const auto& [counter, value] : summary.extraInfo() ) {
+    const auto key = std::make_pair( odin.bunchCrossingType(), counter );
+    ++m_value.at( key )[value];
+    m_time_mean.at( key )[time] += value;
+    m_time_pnz.at( key )[time] += ( value > 0 );
+  }
+  if ( msgLevel( MSG::VERBOSE ) ) {
+    verbose() << "Counters: {";
+    for ( const auto& [counter, value] : summary.extraInfo() ) { verbose() << counter << ':' << value << ", "; }
+    verbose() << '}' << endmsg;
+  }
+}
diff --git a/Hlt/HltDAQ/src/component/HltLumiWriter.cpp b/Hlt/HltDAQ/src/component/HltLumiWriter.cpp
index 94575e875dd99fb8c5d5822a80155bf0b7a8f316..747074a13e0464ad0285f834a872c37027c506f5 100644
--- a/Hlt/HltDAQ/src/component/HltLumiWriter.cpp
+++ b/Hlt/HltDAQ/src/component/HltLumiWriter.cpp
@@ -1,5 +1,5 @@
 /*****************************************************************************\
-* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+* (c) Copyright 2000-2022 CERN for the benefit of the LHCb Collaboration      *
 *                                                                             *
 * This software is distributed under the terms of the GNU General Public      *
 * Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
@@ -9,8 +9,8 @@
 * or submit itself to any jurisdiction.                                       *
 \*****************************************************************************/
 #include "Event/HltLumiSummary.h"
-#include "Event/LumiCounters.h"
 #include "Event/RawEvent.h"
+#include "Kernel/IIndexedLumiSchemaSvc.h"
 #include "Kernel/STLExtensions.h"
 #include "LHCbAlgs/Transformer.h"
 
@@ -34,17 +34,23 @@ public:
 
   //=============================================================================
   // Main execution
-  //  Fill the data bank, structure: Key (upper 16 bits) + value
+  //  Fill the data bank, structure depends on encoding key
   //=============================================================================
   LHCb::RawBank::View operator()( const LHCb::HltLumiSummary& summary ) const override {
+    if ( summary.extraInfo().find( "encodingKey" ) == summary.extraInfo().end() ) {
+      throw std::runtime_error( "HltLumiSummary does not contain an encoding key" );
+    }
+    auto encodingKey = summary.extraInfo().at( "encodingKey" );
+    auto lumi_schema = m_svc->lumiCounters( encodingKey, 0 );
 
     std::vector<unsigned int> bank;
-    bank.reserve( 20 );
-    for ( const auto& info : summary.extraInfo() ) {
-      bank.push_back( ( std::min( info.first, 0xFFFF ) << 16 ) | ( std::min( info.second, 0xFFFF ) & 0xFFFF ) );
-      if ( msgLevel( MSG::VERBOSE ) ) {
-        auto word = bank.back();
-        verbose() << format( " %8x %11d %11d %11d ", word, word, word >> 16, word & 0xFFFF ) << endmsg;
+    bank.resize( lumi_schema.size / 4 );
+    for ( auto cntr : lumi_schema.counters ) {
+      if ( summary.extraInfo().find( cntr.name ) != summary.extraInfo().end() ) {
+        auto val = summary.extraInfo().at( cntr.name );
+        writeCounter( cntr.offset, cntr.size, &bank.front(), val );
+      } else {
+        warning() << "Field " << cntr.name << " ignored by lumiSummary schema " << encodingKey << endmsg;
       }
     }
 
@@ -54,7 +60,7 @@ public:
     }
 
     // set source, type, version
-    rawEvent->addBank( 0, LHCb::RawBank::HltLumiSummary, 0, bank );
+    rawEvent->addBank( 0, LHCb::RawBank::HltLumiSummary, 2, bank );
 
     m_totDataSize += bank.size();
 
@@ -73,6 +79,25 @@ public:
 
     return rawEvent->banks( LHCb::RawBank::HltLumiSummary );
   }
+
+  void writeCounter( unsigned offset, unsigned size, unsigned* target, unsigned value ) const {
+    // Check value fits within size bits
+    if ( size < ( 8 * sizeof( unsigned ) ) && value >= ( 1u << size ) ) { return; }
+
+    // Separate offset into a word part and bit part
+    unsigned word      = offset / ( 8 * sizeof( unsigned ) );
+    unsigned bitoffset = offset % ( 8 * sizeof( unsigned ) );
+
+    // Check size and offset line up with word boundaries
+    if ( bitoffset + size > ( 8 * sizeof( unsigned ) ) ) { return; }
+
+    // Apply the value to the matching bits
+    unsigned mask = ( ( 1ul << size ) - 1 ) << bitoffset;
+    target[word]  = ( target[word] & ~mask ) | ( ( value << bitoffset ) & mask );
+  }
+
+private:
+  ServiceHandle<IIndexedLumiSchemaSvc> m_svc{this, "DecoderMapping", "HltANNSvc"};
 };
 
 DECLARE_COMPONENT( HltLumiWriter )
diff --git a/Hlt/HltDAQ/src/component/HltRoutingBitsFilter.cpp b/Hlt/HltDAQ/src/component/HltRoutingBitsFilter.cpp
index 1d3cd56abbfa4687d53e4a84102b1dcdbeec4860..2fbdeb9ef4879adf60dd9c9909c8be6674df39ee 100644
--- a/Hlt/HltDAQ/src/component/HltRoutingBitsFilter.cpp
+++ b/Hlt/HltDAQ/src/component/HltRoutingBitsFilter.cpp
@@ -60,7 +60,9 @@ bool HltRoutingBitsFilter::operator()( const LHCb::RawEvent& rawEvent ) const {
     ++m_unexpectedRawbanks;
     return m_passOnError;
   }
-  if ( banks[0]->size() != 3 * sizeof( unsigned int ) ) {
+  if ( banks[0]->size() != 3 * sizeof( unsigned int ) &&
+       banks[0]->size() != 4 * sizeof( unsigned int ) // FIXME: should we ever write the 4th word into HltRoutingBits?
+  ) {
     ++m_unexpectedRawbanksSize;
     return m_passOnError;
   }
diff --git a/Hlt/HltDAQ/tests/options/lumi_decoding.py b/Hlt/HltDAQ/tests/options/lumi_decoding.py
new file mode 100644
index 0000000000000000000000000000000000000000..61e58fde70cb1fa5b66d255f39bba325ab8bad7f
--- /dev/null
+++ b/Hlt/HltDAQ/tests/options/lumi_decoding.py
@@ -0,0 +1,55 @@
+###############################################################################
+# (c) Copyright 2022 CERN for the benefit of the LHCb Collaboration           #
+#                                                                             #
+# This software is distributed under the terms of the GNU General Public      #
+# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
+#                                                                             #
+# In applying this licence, CERN does not waive the privileges and immunities #
+# granted to it by virtue of its status as an Intergovernmental Organization  #
+# or submit itself to any jurisdiction.                                       #
+###############################################################################
+import logging
+from Gaudi.Configuration import VERBOSE, DEBUG
+from PRConfig.TestFileDB import test_file_db
+
+from PyConf.application import (
+    default_raw_event,
+    configure_input,
+    configure,
+    make_odin,
+    CompositeNode,
+)
+from PyConf.Algorithms import (
+    HltRoutingBitsFilter,
+    HltLumiSummaryDecoder,
+    HltLumiSummaryMonitor,
+    PrintHeader,
+)
+
+options = test_file_db["hltlumisummary-raw-data-v1"].make_lbexec_options(
+    simulation=True,
+    dddb_tag="dddb-20210617",
+    conddb_tag="sim-20210617-vc-md100",
+    python_logging_level=logging.WARNING,
+    evt_max=5000,
+    histo_file='lumi_decoding_histo.root',
+)
+
+configure_input(options)  # must call this before calling default_raw_event
+odin = make_odin()
+
+rb_filter = HltRoutingBitsFilter(
+    RawEventLocations=default_raw_event(["HltRoutingBits"]),
+    RequireMask=(1 << 1, 0, 0),
+    PassOnError=False)
+summary = HltLumiSummaryDecoder(
+    RawEventLocations=default_raw_event(
+        ["HltLumiSummary"]), ).OutputContainerName
+monitor = HltLumiSummaryMonitor(Input=summary, ODIN=odin, OutputLevel=VERBOSE)
+print_event = PrintHeader(ODINLocation=odin)
+
+top_node = CompositeNode("Top", [rb_filter, print_event, monitor])
+configure(options, top_node)
+
+from Configurables import LHCb__DetDesc__ReserveDetDescForEvent as reserveIOV
+reserveIOV("reserveIOV").PreloadGeometry = False
diff --git a/Hlt/HltDAQ/tests/qmtest/hltdaq.qms/lumi_decoding.qmt b/Hlt/HltDAQ/tests/qmtest/hltdaq.qms/lumi_decoding.qmt
new file mode 100644
index 0000000000000000000000000000000000000000..ec072b4b43cf162b24981c74635e0d73c0d44b42
--- /dev/null
+++ b/Hlt/HltDAQ/tests/qmtest/hltdaq.qms/lumi_decoding.qmt
@@ -0,0 +1,29 @@
+<?xml version="1.0" ?><!DOCTYPE extension  PUBLIC '-//QM/2.3/Extension//EN'  'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'>
+<!--
+    (c) Copyright 2022 CERN for the benefit of the LHCb Collaboration
+
+    This software is distributed under the terms of the GNU General Public
+    Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".
+
+    In applying this licence, CERN does not waive the privileges and immunities
+    granted to it by virtue of its status as an Intergovernmental Organization
+    or submit itself to any jurisdiction.
+-->
+<!--
+Check that the HltLumiSummary decoding works.
+-->
+<extension class="GaudiTest.GaudiExeTest" kind="test">
+  <argument name="program"><text>gaudirun.py</text></argument>
+  <argument name="args"><set>
+    <text>../options/lumi_decoding.py</text>
+  </set></argument>
+  <argument name="use_temp_dir"><enumeral>true</enumeral></argument>
+  <argument name="reference"><text>../refs/lumi_decoding.ref</text></argument>
+  <argument name="validator"><text>
+
+from GaudiConf.QMTest.LHCbExclusions import preprocessor
+validateWithReference(preproc=preprocessor)
+
+</text></argument>
+</extension>
+
diff --git a/Hlt/HltDAQ/tests/refs/lumi_decoding.ref b/Hlt/HltDAQ/tests/refs/lumi_decoding.ref
new file mode 100644
index 0000000000000000000000000000000000000000..b67e9d4a8134ae69e9f32afc1c20c1f791f1e8d6
--- /dev/null
+++ b/Hlt/HltDAQ/tests/refs/lumi_decoding.ref
@@ -0,0 +1,84 @@
+ApplicationMgr    SUCCESS
+====================================================================================================================================
+====================================================================================================================================
+ApplicationMgr       INFO Application Manager Configured successfully
+DetectorPersistencySvc                 INFO Added successfully Conversion service:XmlCnvSvc
+DetectorDataSvc                     SUCCESS Detector description database: git:/lhcb.xml
+HLTControlFlowMgr                      INFO Start initialization
+RootHistSvc                            INFO Writing ROOT histograms to: lumi_decoding_histo.root
+HistogramPersistencySvc                INFO Added successfully Conversion service:RootHistSvc
+HltLumiSummaryMonitor               VERBOSE ServiceLocatorHelper::service: found service EventDataSvc
+HltLumiSummaryMonitor               VERBOSE ServiceLocatorHelper::service: found service TimelineSvc
+HltLumiSummaryMonitor               VERBOSE ServiceLocatorHelper::service: found service EventDataSvc
+HltLumiSummaryMonitor                 DEBUG input handles: 2
+HltLumiSummaryMonitor                 DEBUG output handles: 0
+HltLumiSummaryMonitor                 DEBUG Data Deps for HltLumiSummaryMonitor
+  + INPUT  '/Event/HltLumiSummaryDecoder/OutputContainerName'
+  + INPUT  '/Event/createODIN/ODIN'
+HLTControlFlowMgr                      INFO Concurrency level information:
+HLTControlFlowMgr                      INFO  o Number of events slots: 1
+HLTControlFlowMgr                      INFO  o TBB thread pool size:  'ThreadPoolSize':1
+ApplicationMgr                         INFO Application Manager Initialized successfully
+ApplicationMgr                         INFO Application Manager Started successfully
+EventSelector                          INFO Stream:EventSelector.DataStreamTool_1 Def:DATAFILE='mdf:root://eoslhcb.cern.ch//eos/lhcb/user/e/edallocc/lumi/raw_data/Run_0000249421_20221015-160520-203_HCEB04_0224.mdf' SVC='LHCb::MDFSelector' OPT='READ' IgnoreChecksum='YES'
+HLTControlFlowMgr                      INFO Will measure time between events 500 and 4500 (stop might be some events later)
+HLTControlFlowMgr                      INFO Starting loop on events
+EventSelector.DataStreamTool_1         INFO Compression:0 Checksum:0
+EventSelector                       SUCCESS Reading Event record 1. Record number within stream 1: 1
+EventPersistencySvc                    INFO Added successfully Conversion service:RootCnvSvc
+EventPersistencySvc                    INFO Added successfully Conversion service:LHCb::RawDataCnvSvc
+PrintHeader                            INFO Run 249421, Event 729764180
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:7, MuonHitsM2R3:3, MuonHitsM3R2:5, MuonHitsM3R3:0, SciFiClusters:338, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729792534
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:19, MuonHitsM2R3:12, MuonHitsM3R2:2, MuonHitsM3R3:0, SciFiClusters:796, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729793455
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:0, GEC:1, MuonHitsM2R2:2, MuonHitsM2R3:1, MuonHitsM3R2:4, MuonHitsM3R3:0, SciFiClusters:256, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729794524
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:2, GEC:1, MuonHitsM2R2:7, MuonHitsM2R3:1, MuonHitsM3R2:3, MuonHitsM3R3:0, SciFiClusters:241, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729796040
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:21, MuonHitsM2R3:5, MuonHitsM3R2:1, MuonHitsM3R3:0, SciFiClusters:449, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729768913
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:1, GEC:1, MuonHitsM2R2:2, MuonHitsM2R3:4, MuonHitsM3R2:2, MuonHitsM3R3:0, SciFiClusters:239, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729770819
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:11, MuonHitsM2R3:1, MuonHitsM3R2:5, MuonHitsM3R3:2, SciFiClusters:387, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729801532
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:15, MuonHitsM2R3:6, MuonHitsM3R2:2, MuonHitsM3R3:0, SciFiClusters:250, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729801796
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:8, MuonHitsM2R3:3, MuonHitsM3R2:3, MuonHitsM3R3:2, SciFiClusters:1221, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729772288
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:8, MuonHitsM2R3:2, MuonHitsM3R2:4, MuonHitsM3R3:2, SciFiClusters:737, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729772392
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:2, GEC:1, MuonHitsM2R2:5, MuonHitsM2R3:1, MuonHitsM3R2:0, MuonHitsM3R3:0, SciFiClusters:233, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729803627
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:6, MuonHitsM2R3:7, MuonHitsM3R2:5, MuonHitsM3R3:0, SciFiClusters:288, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729774219
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:13, MuonHitsM2R3:7, MuonHitsM3R2:18, MuonHitsM3R3:6, SciFiClusters:1625, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 729805808
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:0, GEC:1, MuonHitsM2R2:3, MuonHitsM2R3:2, MuonHitsM3R2:1, MuonHitsM3R3:2, SciFiClusters:236, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 736231706
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:40, MuonHitsM2R3:12, MuonHitsM3R2:8, MuonHitsM3R3:7, SciFiClusters:1619, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 736203567
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:57, MuonHitsM2R3:14, MuonHitsM3R2:1, MuonHitsM3R3:10, SciFiClusters:2446, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 736204556
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:13, MuonHitsM2R3:4, MuonHitsM3R2:3, MuonHitsM3R3:0, SciFiClusters:353, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 736204768
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:1, GEC:1, MuonHitsM2R2:16, MuonHitsM2R3:2, MuonHitsM3R2:2, MuonHitsM3R3:3, SciFiClusters:276, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+PrintHeader                            INFO Run 249421, Event 736206167
+HltLumiSummaryMonitor               VERBOSE Counters: {BCIDHigh:0, BCIDLow:0, BXType:3, GEC:1, MuonHitsM2R2:12, MuonHitsM2R3:7, MuonHitsM3R2:8, MuonHitsM3R3:0, SciFiClusters:1192, T0High:0, T0Low:0, VeloTracks:0, VeloVertices:0, }
+ApplicationMgr                         INFO Application Manager Stopped successfully
+HLTControlFlowMgr                      INFO
+HLTControlFlowMgr                      INFO StateTree: CFNode   #executed  #passed
+LAZY_AND: Top                                 #=5000    Sum=19          Eff=|(0.3800000 +- 0.0870122)%|
+ HltRoutingBitsFilter/HltRoutingBitsFilter    #=5000    Sum=19          Eff=|(0.3800000 +- 0.0870122)%|
+ PrintHeader/PrintHeader                      #=19      Sum=19          Eff=|( 100.0000 +- 0.00000 )%|
+ HltLumiSummaryMonitor/HltLumiSummaryMonitor  #=19      Sum=19          Eff=|( 100.0000 +- 0.00000 )%|
+HLTControlFlowMgr                      INFO Histograms converted successfully according to request.
+ToolSvc                                INFO Removing all tools created by ToolSvc
+ApplicationMgr                         INFO Application Manager Finalized successfully
+ApplicationMgr                         INFO Application Manager Terminated successfully
+HltRoutingBitsFilter                   INFO Number of counters : 1
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ |*"#accept"                                       |      5000 |         19 |(0.3800000 +- 0.08701218)% |
+PrintHeader                            INFO Number of counters : 1
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "EventCount"                                    |        19 |
diff --git a/Hlt/HltServices/src/ANNSvcBase.h b/Hlt/HltServices/src/ANNSvcBase.h
index cdff5287b1c9d5b15bcad4ea5ff6559c4ffb2ffe..7d336e988bc6491a58a7a664ade424acc501f427 100644
--- a/Hlt/HltServices/src/ANNSvcBase.h
+++ b/Hlt/HltServices/src/ANNSvcBase.h
@@ -11,6 +11,7 @@
 #pragma once
 #include "GaudiKernel/Service.h"
 #include "Kernel/IIndexedANNSvc.h"
+#include "Kernel/IIndexedLumiSchemaSvc.h"
 #include "Kernel/SynchronizedValue.h"
 #include <map>
 #include <string>
@@ -36,7 +37,7 @@ namespace ANNSvcBase_details {
 
 } // namespace ANNSvcBase_details
 
-class ANNSvcBase : public extends<Service, IIndexedANNSvc> {
+class ANNSvcBase : public extends<Service, IIndexedANNSvc, IIndexedLumiSchemaSvc> {
 
 public:
   using extends::extends;
@@ -67,14 +68,28 @@ public:
     return *ptr;
   }
 
+  lumi_schema_t const& lumiCounters( unsigned key, unsigned version ) const override final {
+    auto ptr = m_lumi.with_lock( [&]( std::map<std::pair<unsigned, unsigned>, lumi_schema_t>& lumi ) {
+      auto j = lumi.find( {key, version} );
+      if ( j != lumi.end() ) return &j->second;
+      auto [k, ok] = lumi.emplace( std::pair{key, version}, fetch_lumi( key, version ) );
+      return ok ? &k->second : nullptr;
+    } );
+    if ( !ptr ) throw GaudiException{"Failed to add to lumi schema map", __PRETTY_FUNCTION__, StatusCode::FAILURE};
+    return *ptr;
+  }
+
 private:
   Gaudi::Property<bool> m_allow_zero_as_key{this, "AllowZeroAsKey", false};
 
-  virtual map_t fetch( unsigned int tck, const Gaudi::StringKey& major ) const = 0;
+  virtual map_t         fetch( unsigned int tck, const Gaudi::StringKey& major ) const = 0;
+  virtual lumi_schema_t fetch_lumi( unsigned int key, unsigned int version ) const     = 0;
 
   mutable std::array<LHCb::cxx::SynchronizedValue<std::map<unsigned int, map_t>>, ANNSvcBase_details::s_majors.size()>
       m_maps;
   mutable std::array<LHCb::cxx::SynchronizedValue<std::map<unsigned int, inv_map_t>>,
                      ANNSvcBase_details::s_majors.size()>
       m_inv_maps;
+
+  mutable LHCb::cxx::SynchronizedValue<std::map<std::pair<unsigned, unsigned>, lumi_schema_t>> m_lumi;
 };
diff --git a/Hlt/HltServices/src/GitANNSvc.cpp b/Hlt/HltServices/src/GitANNSvc.cpp
index c84853e5689b0d69df65395ea78a93361989d1e2..ab06e384d1b4fe4e690dd197524f24f3c466618b 100644
--- a/Hlt/HltServices/src/GitANNSvc.cpp
+++ b/Hlt/HltServices/src/GitANNSvc.cpp
@@ -96,30 +96,29 @@ class GitANNSvc : public ANNSvcBase {
   Gaudi::Property<std::string> m_fmt{this, "RefFormatter", "{0}:ann/json/{1:.2}/{1}.json"}; // 0=tag, 1=key, 2=label
   Gaudi::Property<std::string> m_altfmt{this, "AltRefFormatter", "key-{1}:ann/json/{1:.2}/{1}.json"};
 
+  Gaudi::Property<std::map<unsigned int, std::string>> m_lumi_key2JSON{this, "LumiOverrule", {}};
+  Gaudi::Property<std::string>                         m_lumi_fmt{this, "LumiRefFormatter",
+                                          "{0}:luminosity_counters/json/{1:.2}/{1}.json"}; // 0=tag, 1=key, 2=version
+
   std::optional<std::string> fetch_from_repo( std::string const& repo, std::string const& name ) const {
     if ( repo.empty() ) error() << "no repo specified..." << endmsg;
     auto g = git{repo};
     if ( !g ) throw GaudiException( "unable to open git repo: " + repo, __PRETTY_FUNCTION__, StatusCode::FAILURE );
     if ( msgLevel( MSG::DEBUG ) ) {
-      debug() << "fetch_from_repos: trying to get " << name << " from " << repo << endmsg;
+      debug() << "fetch_from_repo: trying to get " << name << " from " << repo << endmsg;
     }
     return g.read( name );
   }
 
-  std::optional<std::string> fetch_from_repos( std::string_view fmt, std::string_view key,
-                                               std::string_view major ) const {
+  template <typename F>
+  std::optional<std::string> fetch_from_repos( F&& fmt ) const {
     for ( const auto& [repo, tag] : m_repo.value() ) {
-      if ( auto s = fetch_from_repo( repo, fmt::format( fmt, tag, key, major ) ); s ) return s;
+      if ( auto s = fetch_from_repo( repo, fmt( tag ) ); s ) return s;
     }
     return std::nullopt;
   }
 
-  std::optional<std::string> fetch_from_repos( std::string_view key, std::string_view major ) const {
-    auto s = fetch_from_repos( m_fmt, key, major );
-    return s ? s : fetch_from_repos( m_altfmt, key, major );
-  }
-
-  std::optional<nlohmann::json> fetch_json( unsigned int key, const Gaudi::StringKey& major ) const {
+  std::optional<nlohmann::json> fetch_ann_json( unsigned int key, const Gaudi::StringKey& major ) const {
     if ( auto io = m_key2JSON.find( key ); io != m_key2JSON.end() ) {
       warning() << "key " << fmt::format( "0x{:08x}", key ) << " has an explicitly configured overrule -- using that..."
                 << endmsg;
@@ -132,10 +131,11 @@ class GitANNSvc : public ANNSvcBase {
     auto id = ( i != m_key2commit.end() ? i->second : fmt::format( "{:08x}", key ) );
 
     if ( msgLevel( MSG::DEBUG ) )
-      debug() << "fetch_json( " << key << " -> " << id << " ) from repo " << m_repo.value() << endmsg;
-
-    auto s = fetch_from_repos( id, major.str() );
+      debug() << "fetch_ann_json( " << key << " -> " << id << " ) from repo " << m_repo.value() << endmsg;
 
+    auto s = fetch_from_repos( [&]( auto const& tag ) { return fmt::format( m_fmt.value(), tag, id, major.str() ); } );
+    if ( !s )
+      s = fetch_from_repos( [&]( auto const& tag ) { return fmt::format( m_altfmt.value(), tag, id, major.str() ); } );
     if ( !s ) return std::nullopt;
     auto json = nlohmann::json::parse( *s, nullptr, false );
     if ( json.is_discarded() ) return std::nullopt;
@@ -144,7 +144,7 @@ class GitANNSvc : public ANNSvcBase {
 
   template <typename Inserter>
   void fetch_( unsigned int key, const Gaudi::StringKey& major, Inserter inserter ) const {
-    auto json = fetch_json( key, major );
+    auto json = fetch_ann_json( key, major );
     if ( !json )
       throw GaudiException( fmt::format( "unable to obtain json for key {:08x} from repositories {}", key,
                                          Gaudi::Utils::toString( m_repo.value() ) ),
@@ -162,6 +162,57 @@ class GitANNSvc : public ANNSvcBase {
     return m;
   }
 
+  std::optional<nlohmann::json> fetch_lumi_json( unsigned int key ) const {
+    if ( auto io = m_lumi_key2JSON.find( key ); io != m_lumi_key2JSON.end() ) {
+      warning() << "lumi key " << fmt::format( "0x{:08x}", key )
+                << " has an explicitly configured overrule -- using that..." << endmsg;
+      auto json = nlohmann::json::parse( io->second, nullptr, false );
+      if ( json.is_discarded() ) return std::nullopt;
+      return json;
+    }
+
+    auto id = fmt::format( "{:08x}", key );
+
+    if ( msgLevel( MSG::DEBUG ) )
+      debug() << "fetch_lumi_json( " << key << " -> " << id << " ) from repo " << m_repo.value() << endmsg;
+
+    auto s = fetch_from_repos( [&]( auto const& tag ) { return fmt::format( m_lumi_fmt.value(), tag, id ); } );
+    if ( !s ) return std::nullopt;
+    auto json = nlohmann::json::parse( *s, nullptr, false );
+    if ( json.is_discarded() ) return std::nullopt;
+    return json;
+  }
+
+  lumi_schema_t fetch_lumi( unsigned int key, unsigned int version ) const override {
+    if ( msgLevel( MSG::DEBUG ) ) debug() << "fetch lumi(" << key << "," << version << ")" << endmsg;
+    auto json = fetch_lumi_json( key );
+    if ( !json )
+      throw GaudiException( fmt::format( "unable to obtain json for key {:08x} from repositories {}", key,
+                                         Gaudi::Utils::toString( m_repo.value() ) ),
+                            "GitANNSvc::fetch_lumi", StatusCode::FAILURE );
+    auto vs = json->find( "version" );
+    if ( vs == json->end() || vs->template get<unsigned>() != version ) {
+      // must have correct version!!!)
+      error() << "wrong version" << endmsg;
+      return {};
+    }
+    auto sz = json->find( "size" );
+    if ( sz == json->end() ) {
+      error() << "no size" << endmsg;
+      return {};
+    }
+    auto j = json->find( "counters" );
+    if ( j == json->end() ) {
+      error() << "no counters" << endmsg;
+      return {};
+    }
+    lumi_schema_t m;
+    m.size = sz->template get<unsigned>();
+    std::transform( j->begin(), j->end(), std::back_inserter( m.counters ),
+                    []( const nlohmann::json& k ) { return IIndexedLumiSchemaSvc::from_json( k ); } );
+    return m;
+  }
+
 public:
   using ANNSvcBase::ANNSvcBase;
 };
diff --git a/Hlt/HltServices/src/TCKANNSvc.cpp b/Hlt/HltServices/src/TCKANNSvc.cpp
index 1ebdc229d5c057b8fd4d5d0dfae13e7af675097a..40b1037851d0e3e39523969bc1a1a44f785bc8af 100644
--- a/Hlt/HltServices/src/TCKANNSvc.cpp
+++ b/Hlt/HltServices/src/TCKANNSvc.cpp
@@ -54,6 +54,11 @@ namespace std {
 
 class TCKANNSvc : public ANNSvcBase {
 
+  lumi_schema_t fetch_lumi( unsigned int, unsigned int ) const override {
+    throw GaudiException( "fetch_lumi not supported -- please use different service implementation",
+                          __PRETTY_FUNCTION__, StatusCode::FAILURE );
+    return {};
+  }
   map_t fetch( unsigned int tck, const Gaudi::StringKey& major ) const override {
     TCK _tck( tck );
     _tck.normalize();
diff --git a/Kernel/HltInterfaces/include/Kernel/IIndexedLumiSchemaSvc.h b/Kernel/HltInterfaces/include/Kernel/IIndexedLumiSchemaSvc.h
new file mode 100644
index 0000000000000000000000000000000000000000..36df796eef1fd972d6b6271b66387dd4080dcbda
--- /dev/null
+++ b/Kernel/HltInterfaces/include/Kernel/IIndexedLumiSchemaSvc.h
@@ -0,0 +1,48 @@
+/*****************************************************************************\
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+#pragma once
+
+// Include files
+#include "GaudiKernel/INamedInterface.h"
+
+/** @class ITCKANNSvc ITCKANNSvc.h
+ *
+ * return the layout of a lumi bank given a key
+ *
+ *  @author Gerhard Raven
+ *  @date   2014-05-29
+ */
+
+struct IIndexedLumiSchemaSvc : extend_interfaces<INamedInterface> {
+public:
+  /// Return the interface ID
+  DeclareInterfaceID( IIndexedLumiSchemaSvc, 1, 0 );
+
+  struct CounterDefinition {
+    std::string name;
+    unsigned    size;
+    unsigned    offset;
+  };
+
+  template <typename JSON>
+  static CounterDefinition from_json( const JSON& json ) {
+    return CounterDefinition{json["name"].template get<std::string>(), json["size"].template get<unsigned>(),
+                             json["offset"].template get<unsigned>()};
+  }
+
+  struct lumi_schema_t {
+    unsigned                       size;
+    std::vector<CounterDefinition> counters;
+  };
+
+  // given a bank of version `version`, and a key `key`, return which counters can be found where
+  virtual lumi_schema_t const& lumiCounters( unsigned key, unsigned version ) const = 0;
+};