diff --git a/src/videoag_common/api_object/fields/relationship_fields.py b/src/videoag_common/api_object/fields/relationship_fields.py
index 44034525e3c8434b494474336e464cba41b98231..a324c8b8fe33a96a50832b2c8f82286976c29fb9 100644
--- a/src/videoag_common/api_object/fields/relationship_fields.py
+++ b/src/videoag_common/api_object/fields/relationship_fields.py
@@ -48,21 +48,6 @@ class ApiAbstractRelationshipField(ApiAbstractColumnField[_O], Generic[_O], ABC)
         # Relationship might have extra join conditions, and then it is always possible to get null values. A constraint
         # is often not feasible. In this case we just return None instead of throwing an exception
         return True
-    
-    def _serialize_relationship(self, obj, args):
-        if not self.data_foreign_in_context:
-            return self._foreign_class.serialize_object_args(obj, args)
-        id = obj.id
-        context_name = f"{self._foreign_class.id}_context"
-        if not hasattr(args, context_name):
-            raise Exception(f"Missing serialization argument '{context_name}'")
-        context = getattr(args, context_name)
-        if not isinstance(context, dict):
-            raise Exception(f"serialization argument '{context_name}' must be a dict")
-        if str(id) not in context:
-            context[str(id)] = None  # Indicate that object is being serialized. Prevent loops
-            context[str(id)] = self._foreign_class.serialize_object_args(obj, args)
-        return id
 
 
 class ApiMany2OneRelationshipField(ApiAbstractRelationshipField[_O], Generic[_O]):
@@ -107,7 +92,7 @@ class ApiMany2OneRelationshipField(ApiAbstractRelationshipField[_O], Generic[_O]
         return "int" if self.data_foreign_in_context else self._foreign_class.id
     
     def _data_db_value_to_json(self, db_value, args) -> JsonTypes:
-        return self._serialize_relationship(db_value, args)
+        return self._foreign_class.serialize_object_args(db_value, args, to_context=self.data_foreign_in_context)
     
     def _config_db_value_to_json(self, db_value) -> JsonTypes:
         return db_value.id
@@ -160,7 +145,7 @@ class ApiAny2ManyRelationshipField(ApiAbstractRelationshipField[_O], Generic[_O]
     
     def _data_db_value_to_json(self, db_value, args) -> JsonTypes:
         return list(map(
-            lambda obj: self._serialize_relationship(obj, args),
+            lambda obj: self._foreign_class.serialize_object_args(obj, args, to_context=self.data_foreign_in_context),
             db_value
         ))
     
diff --git a/src/videoag_common/api_object/fields/special_fields.py b/src/videoag_common/api_object/fields/special_fields.py
index 3853695ee9cbb10c664b63d1005de3a5278b4c91..b46d517e464af8cc65ac0e122d1686c7bff3bd66 100644
--- a/src/videoag_common/api_object/fields/special_fields.py
+++ b/src/videoag_common/api_object/fields/special_fields.py
@@ -11,14 +11,19 @@ _O = TypeVar("_O", bound="ApiObject")
 class ApiDataMethodField(ApiDataField[_O], Generic[_O]):
     
     # TODO get type id from function return type
-    def __init__(self, type_id: str, **kwargs):
+    def __init__(self,
+                 type_id: str,
+                 data_foreign_in_context: bool = False,
+                 **kwargs):
         super().__init__(**kwargs)
         self._data_func: Callable[[_O, Any], JsonTypes] or None = None
         self._serializer = None
-        self._type_id: str = type_id
+        self._type_id = type_id
+        self._data_type_id = None
+        self._data_foreign_in_context = data_foreign_in_context
     
     def data_get_type_id(self) -> str:
-        return self._type_id
+        return self._data_type_id
     
     def post_init(self, context: FieldContext):
         super().post_init(context)
@@ -27,7 +32,7 @@ class ApiDataMethodField(ApiDataField[_O], Generic[_O]):
         self._data_func = context.own_member
         
         from ..object import api_get_value_serializer_function
-        self._serializer = api_get_value_serializer_function(context.all_classes, self._type_id)
+        self._data_type_id, self._serializer = api_get_value_serializer_function(context.all_classes, self._type_id, self._data_foreign_in_context)
     
     def data_get_value(self, object_db: _O, args) -> JsonTypes:
         kwargs = {}
diff --git a/src/videoag_common/api_object/object.py b/src/videoag_common/api_object/object.py
index 4dcae98fac3f56c7c6c4ab0cb6906f0993ac630d..f392e21120546c902c61c573415f53bc1a5c14aa 100644
--- a/src/videoag_common/api_object/object.py
+++ b/src/videoag_common/api_object/object.py
@@ -139,13 +139,18 @@ class ApiObject:
     
     def serialize(self, **kwargs):
         return self.__api_class__.serialize(self, **kwargs)
+    
+    def serialize_to_context(self, **kwargs):
+        return self.__api_class__.serialize_to_context(self, **kwargs)
 
 
 ApiValueTypes = ApiObject or ApiValueObject or str or int or bool or None or list["ApiValueType"]
 
 
-def api_get_value_serializer_function(all_classes: dict[str, "ApiObjectClass"], type_id: str) \
-        -> Callable[[ApiValueTypes, ArgAttributeObject], JsonTypes]:
+def api_get_value_serializer_function(
+        all_classes: dict[str, "ApiObjectClass"],
+        type_id: str,
+        foreign_in_context: bool) -> tuple[str, Callable[[ApiValueTypes, ArgAttributeObject], JsonTypes]]:
     full_type_id = type_id
     is_array = False
     if type_id.endswith("[]"):
@@ -155,6 +160,9 @@ def api_get_value_serializer_function(all_classes: dict[str, "ApiObjectClass"],
     _disable_value_object_registration = True
     
     if type_id in API_VALUE_OBJECT_CLASSES_BY_ID:
+        if foreign_in_context:
+            raise ValueError("foreign_in_context is set but this is not an object with an id")
+        
         if type_id in all_classes:
             raise ValueError(f"Duplicate id '{type}' in API classes and value classes")
         value_class = API_VALUE_OBJECT_CLASSES_BY_ID[type_id]
@@ -168,26 +176,32 @@ def api_get_value_serializer_function(all_classes: dict[str, "ApiObjectClass"],
     elif type_id in all_classes:
         object_class = all_classes[type_id]
         
+        if foreign_in_context:
+            type_id = "int"
+        
         def serializer(val: ApiValueTypes, args):
             if val is None:
                 return val
             if not isinstance(val, object_class.orm_class):
                 raise TypeError(f"Got object of type '{type(val)}' but it was previously declared to be a '{object_class.orm_class}'")
-            return object_class.serialize_object_args(val, args)
+            return object_class.serialize_object_args(val, args, to_context=foreign_in_context)
     elif type_id in ("string", "integer", "boolean"):
+        if foreign_in_context:
+            raise ValueError("foreign_in_context is set but this is not an object with an id")
+        
         def serializer(val: ApiValueTypes, args):
             return val
     else:
         raise ValueError(f"Unknown type '{full_type_id}' (For ApiValueObject make sure that the __api_id__ attribute is set)")
     
     if not is_array:
-        return serializer
+        return type_id, serializer
     
     def serializer_list(val_list: list[ApiValueTypes], args):
         if val_list is None:
             return val_list
         return list(map(lambda v: serializer(v, args), val_list))
-    return serializer_list
+    return f"{type_id}[]", serializer_list
 
 
 class DeletableApiObject(ApiObject):
diff --git a/src/videoag_common/api_object/object_class.py b/src/videoag_common/api_object/object_class.py
index ec22348d56ac82b9a8e592ce429d466ea238f4d0..8c66e50ed86756dcd1cec7d0688f31b4264b7ac7 100644
--- a/src/videoag_common/api_object/object_class.py
+++ b/src/videoag_common/api_object/object_class.py
@@ -297,23 +297,41 @@ class ApiObjectClass:
         return field_list
     
     def serialize(self, obj, **kwargs) -> dict:
+        return self._serialize_catch_error(obj, to_context=False, **kwargs)
+    
+    def serialize_to_context(self, obj, **kwargs) -> int:
+        return self._serialize_catch_error(obj, to_context=True, **kwargs)
+        
+    def _serialize_catch_error(self, obj, to_context: bool, **kwargs) -> dict or int:
         if not self.enable_data:
             raise Exception("Serialization not enabled")
         try:
-            return self.serialize_object_args(obj, ArgAttributeObject(**kwargs))
+            return self.serialize_object_args(obj, ArgAttributeObject(**kwargs), to_context)
         except AttributeError as e:
             if not str(e).startswith("'ArgAttributeObject' object has no attribute"):
                 raise e
-            raise AttributeError(f"Missing a keyword argument for serialize() of object '{self.id}': " + str(e)) from e
-        
-    def serialize_object_args(self, obj, args) -> dict:
+            raise AttributeError(f"Missing a keyword argument for serialization of object '{self.id}': " + str(e)) from e
+    
+    def serialize_object_args(self, obj, args, to_context: bool = False) -> dict or int:
         if not self.enable_data:
             raise Exception("Serialization not enabled")
-        res = {}
-        for variant_id in self._get_current_variant_set(obj):
-            for field in self._fields_by_variant_by_data_id[variant_id].values():
-                field.add_to_data(obj, res, args)
-        return res
+        if not to_context:
+            res = {}
+            for variant_id in self._get_current_variant_set(obj):
+                for field in self._fields_by_variant_by_data_id[variant_id].values():
+                    field.add_to_data(obj, res, args)
+            return res
+        
+        context_name = f"{self.id}_context"
+        if not hasattr(args, context_name):
+            raise Exception(f"Missing serialization argument '{context_name}'")
+        context = getattr(args, context_name)
+        if not isinstance(context, dict):
+            raise Exception(f"serialization argument '{context_name}' must be a dict")
+        if str(self.id) not in context:
+            context[str(self.id)] = None  # Indicate that object is being serialized. Prevent loops
+            context[str(self.id)] = self.serialize_object_args(obj, args, to_context=False)
+        return self.id
     
     def get_creation_config(self) -> JsonTypes or None:
         if not self.enable_config:
diff --git a/src/videoag_common/objects/course.py b/src/videoag_common/objects/course.py
index 8523a2fc6d56ec6aea104f80fdc634e86ba330f4..bacda60b81b2a4ab65f746fc3b9a7b1293ffb33e 100644
--- a/src/videoag_common/objects/course.py
+++ b/src/videoag_common/objects/course.py
@@ -104,7 +104,6 @@ class Lecture(DeletableApiObject, VisibilityApiObject, ApiViewPermissionsObject,
             include_in_data=True
         )
     )
-    # TODO thumbnail url
     no_recording: Mapped[bool] = api_mapped(
         mapped_column(nullable=False, default=False),
         ApiBooleanField(
diff --git a/src/videoag_common/objects/site.py b/src/videoag_common/objects/site.py
index af906fc98d93831f064136021d2688563c47b26e..02bb717c107e3deb643f7ba893ff5d8dbcaf89a9 100644
--- a/src/videoag_common/objects/site.py
+++ b/src/videoag_common/objects/site.py
@@ -191,8 +191,8 @@ class CourseFeatured(Featured):
     
     @api_include_in_data(
         type_id="course",
-        data_id="course",
-        data_notes="May be null (deleted/not visible). Does not include lectures"
+        data_id="course_id", data_foreign_in_context=True,
+        data_notes="May be null (deleted/not visible). Does not include lectures",
     )
     def _data_course(self, is_mod: bool):
         return self.course if is_mod else self.public_course