diff --git a/src/videoag_common/objects/medium.py b/src/videoag_common/objects/medium.py
index 2bdb82b3fbe24ac9aa74933a1e8ed96ce685aaee..3fd92ab2929f101fa5d35fc2e506e9797436dfa8 100644
--- a/src/videoag_common/objects/medium.py
+++ b/src/videoag_common/objects/medium.py
@@ -38,6 +38,9 @@ _SOURCE_MEDIUM_STATUS_ENUM = create_enum_type(SourceMediumStatus)
 
 # A file which was uploaded and needs to be assigned to a lecture, to be converted to a SourceFileTargetMedium
 class SourceMedium(DeletableApiObject, Base):
+    __api_class__ = ApiObjectClass(
+        config_allow_creation=False
+    )
     __table_args__ = (
         CheckConstraint(
             "NOT tag IS NULL OR lecture_id IS NULL",
@@ -211,6 +214,9 @@ class TargetMedium(DeletableApiObject, Base):
             name="check_video_frame_rate_denominator_not_null"
         ),
     )
+    __api_class__ = ApiObjectClass(
+        config_allow_creation=False
+    )
     
     # Note that these four may NOT be unique. E.g. if one output is generated twice (because another output of the same
     # producer was deleted, etc.)
@@ -459,10 +465,12 @@ class ImageTargetMedium(TargetMedium, FileMedium, SingleImageContainingMedium):
 
 
 class PublishMedium(VisibilityApiObject, DeletableApiObject, Base):
-    __api_data__ = ApiObjectClass(
-        parent_relationship_config_ids=["lecture"]
+    __api_class__ = ApiObjectClass(
+        parent_relationship_config_ids=["lecture"],
+        config_allow_creation=False
     )
     
+    # TODO why do we have the lecture_id if the target_medium already has that?
     lecture_id: Mapped[int] = mapped_column(ForeignKey("lecture.id"), nullable=False, index=True)
     title: Mapped[str] = api_mapped(
         mapped_column(Text(collation=STRING_COLLATION), nullable=False, default=""),