diff --git a/actions/ost-compare-structures-new b/actions/ost-compare-structures-new
index adb998504e42cfad385f7d36ec5fefb898216bd9..f4e562d5dc58ca2ce44b1a2202d14d61e2a11368 100644
--- a/actions/ost-compare-structures-new
+++ b/actions/ost-compare-structures-new
@@ -168,10 +168,11 @@ def _ParseArgs():
         help=("Compute per-residue lDDT scores with default parameterization "
               "and store as key \"local_lddt\". Score for model residue with "
               "number 42 in chain X can be extracted with: "
-              "data[\"local_lddt\"][\"X\"][\"42\"]. Stereochemical irregularities "
-              "affecting lDDT are reported as keys \"model_clashes\", "
-              "\"model_bad_bonds\", \"model_bad_angles\" and the respective "
-              "reference counterparts."))
+              "data[\"local_lddt\"][\"X\"][\"42\"]. If there is an insertion "
+              "code, lets say A, the last key becomes \"42A\" Stereochemical "
+              "irregularities affecting lDDT are reported as keys "
+              "\"model_clashes\", \"model_bad_bonds\", \"model_bad_angles\" "
+              "and the respective reference counterparts."))
 
     parser.add_argument(
         "--cad-score",
@@ -269,7 +270,8 @@ def _ParseArgs():
               "the full patches are represented by the reference are set to "
               "None. Model interface residues are available as key "
               "\"model_interface_residues\", reference interface residues as "
-              "key \"reference_interface_residues\". The respective scores are "
+              "key \"reference_interface_residues\". Residues are represented "
+              "as string in form <num><inscode>. The respective scores are "
               "available as keys \"patch_qs\" and \"patch_dockq\""))
 
     return parser.parse_args()
@@ -330,6 +332,26 @@ def _GetInconsistentResidues(alns):
     lst = [f"{x[0].GetQualifiedName()}-{x[1].GetQualifiedName()}" for x in lst]
     return lst
 
+def _LocalScoresToJSONDict(score_dict):
+    """ Score for model residue with number rnum in chain X can be extracted
+    with: data["local_cad_score"]["X"][rnum]. Convert ResNum object to str for
+    JSON serialization
+    """
+    json_dict = dict()
+    for ch, ch_scores in score_dict.items():
+        json_dict[ch] = {str(rnum): s for rnum, s in ch_scores.items()}
+    return json_dict
+
+def _InterfaceResiduesToJSONDict(interface_dict):
+    """ Interface residues are stored as
+    data["model_interface_residues"]["A"][rnum1, rnum2,...]. Convert ResNum
+    object to str for JSON serialization.
+    """
+    json_dict = dict()
+    for ch, ch_nums in interface_dict.items():
+        json_dict[ch] = [str(rnum) for rnum in ch_nums]
+    return json_dict
+
 def _Process(model, reference, args):
 
 
@@ -350,7 +372,7 @@ def _Process(model, reference, args):
         out["lddt"] = scorer.lddt
 
     if args.local_lddt:
-        out["local_lddt"] = scorer.local_lddt
+        out["local_lddt"] = _LocalScoresToJSONDict(scorer.local_lddt)
 
     if args.lddt or args.local_lddt:
         out["model_clashes"] = [x.ToJSON() for x in scorer.model_clashes]
@@ -364,7 +386,7 @@ def _Process(model, reference, args):
         out["cad_score"] = scorer.cad_score
 
     if args.local_cad_score:
-        out["local_cad_score"] = scorer.local_cad_score
+        out["local_cad_score"] = _LocalScoresToJSONDict(scorer.local_cad_score)
 
     if args.qs_score:
         out["qs_global"] = scorer.qs_global
@@ -394,8 +416,10 @@ def _Process(model, reference, args):
         out["dockq_wave_full"] = scorer.dockq_wave_full
 
     if args.patch_scores:
-        out["model_interface_residues"] = scorer.model_interface_residues
-        out["reference_interface_residues"] = scorer.target_interface_residues
+        out["model_interface_residues"] = \
+        _InterfaceResiduesToJSONDict(scorer.model_interface_residues)
+        out["reference_interface_residues"] = \
+        _InterfaceResiduesToJSONDict(scorer.target_interface_residues)
         out["patch_qs"] = scorer.patch_qs
         out["patch_dockq"] = scorer.patch_dockq
 
diff --git a/modules/mol/alg/pymod/scoring.py b/modules/mol/alg/pymod/scoring.py
index 2b8ecb3bcabbc3af8ddb41ed2e12a4c386ca348a..73d42464ffc54395aee3e77fb43abb007a927a10 100644
--- a/modules/mol/alg/pymod/scoring.py
+++ b/modules/mol/alg/pymod/scoring.py
@@ -974,14 +974,14 @@ class Scorer:
                 local_lddt[cname] = dict()
             if r.HasProp("lddt"):
                 score = round(r.GetFloatProp("lddt"), 3)
-                local_lddt[cname][r.GetNumber().GetNum()] = score
+                local_lddt[cname][r.GetNumber()] = score
             else:
                 # rsc => residue stereo checked...
                 mdl_res = self.stereochecked_model.FindResidue(cname, r.GetNumber())
                 if mdl_res.IsValid():
                     # not covered by trg or skipped in chain mapping procedure
                     # the latter happens if its part of a super short chain
-                    local_lddt[cname][r.GetNumber().GetNum()] = None
+                    local_lddt[cname][r.GetNumber()] = None
                 else:
                     # opt 1: removed by stereochecks => assign 0.0
                     # opt 2: removed by stereochecks AND not covered by ref
@@ -997,9 +997,9 @@ class Scorer:
                                     trg_r = col.GetResidue(0)
                                     break
                     if trg_r is None:
-                        local_lddt[cname][r.GetNumber().GetNum()] = None
+                        local_lddt[cname][r.GetNumber()] = None
                     else:
-                        local_lddt[cname][r.GetNumber().GetNum()] = 0.0
+                        local_lddt[cname][r.GetNumber()] = 0.0
 
         self._lddt = lddt_score
         self._local_lddt = local_lddt
@@ -1149,9 +1149,9 @@ class Scorer:
                 local_cad[cname] = dict()
             if r.HasProp("localcad"):
                 score = round(r.GetFloatProp("localcad"), 3)
-                local_cad[cname][r.GetNumber().GetNum()] = score
+                local_cad[cname][r.GetNumber()] = score
             else:
-                local_cad[cname][r.GetNumber().GetNum()] = None
+                local_cad[cname][r.GetNumber()] = None
 
         self._cad_score = cad_result.globalAA
         self._local_cad_score = local_cad
@@ -1187,7 +1187,7 @@ class Scorer:
         for ch in ent.chains:
             cname = ch.GetName()
             sel = repr_ent.Select(f"(cname={cname} and 8 <> [cname!={cname}])")
-            result[cname] = [r.GetNumber().GetNum() for r in sel.residues]
+            result[cname] = [r.GetNumber() for r in sel.residues]
         return result
 
     def _do_stereochecks(self):
@@ -1229,7 +1229,7 @@ class Scorer:
         :param mdl_ch: Name of chain in *self.model* of residue of interest
         :type mdl_ch: :class:`str`
         :param mdl_rnum: Residue number of residue of interest
-        :type mdl_rnum: :class:`int`
+        :type mdl_rnum: :class:`ost.mol.ResNum`
         :returns: Tuple with 5 elements: 1) :class:`bool` flag whether all residues
                   in *mdl* patches are covered in *trg* 2) mtl_patch_one
                   3) mdl_patch_two 4) trg_patch_one 5) trg_patch_two
@@ -1238,7 +1238,7 @@ class Scorer:
         repr_mdl = self._get_repr_view(self.model.Select("peptide=true"))
     
         # get position for specified residue
-        r = self.model.FindResidue(mdl_ch, mol.ResNum(mdl_rnum))
+        r = self.model.FindResidue(mdl_ch, mdl_rnum)
         if not r.IsValid():
             raise RuntimeError(f"Cannot find residue {mdl_rnum} in chain {mdl_ch}")
         if r.GetName() == "GLY":
@@ -1332,7 +1332,7 @@ class Scorer:
             scores = list()
             for rnum in rnums:
                 score = None
-                r = self.model.FindResidue(cname, mol.ResNum(rnum))
+                r = self.model.FindResidue(cname, rnum)
                 if r.IsValid() and r.GetChemType() == mol.ChemType.AMINOACIDS:
                     full_trg_coverage, mdl_patch_one, mdl_patch_two, \
                     trg_patch_one, trg_patch_two = \
@@ -1349,7 +1349,7 @@ class Scorer:
             scores = list()
             for rnum in rnums:
                 score = None
-                r = self.model.FindResidue(cname, mol.ResNum(rnum))
+                r = self.model.FindResidue(cname, rnum)
                 if r.IsValid() and r.GetChemType() == mol.ChemType.AMINOACIDS:
                     full_trg_coverage, mdl_patch_one, mdl_patch_two, \
                     trg_patch_one, trg_patch_two = \