diff --git lx_dtypes/data/star_upper_gi/config.yaml lx_dtypes/data/star_upper_gi/config.yaml
index 4e50c48..d04d1ce 100644
--- lx_dtypes/data/star_upper_gi/config.yaml
+++ lx_dtypes/data/star_upper_gi/config.yaml
@@ -1,21 +1,22 @@
 name: star_upper_gi
 description: |
   Data types for recording findings, classifications, 
   and interventions during upper gastrointestinal (GI)
   endoscopy procedures.
   Based on the ESGE STAR Upper GI qualitiy improvement initiative 
   (Esposito et al 2025, doi.org/10.1055/a-2652-8941).
 version: 0.1.1
 depends_on:
   - lx_classification_choices
 modules:
   - lx_units # Externalized module to re-use
 
 data:
   files:
     - "./main.yaml" # Contains the data fixture for gastroscopy
+    - "./report_templates.yaml"
   dirs: # Subdirectories containing .yaml files for findings, classifications, etc.
     - "./star_u_gi_descriptors/"
     - "./star_u_gi_choices/"
     - "./star_u_gi_classifications/"
     - "./star_u_gi_findings/"
diff --git lx_dtypes/django/api/findings_routes.py lx_dtypes/django/api/findings_routes.py
index bae0b45..d1ea36f 100644
--- lx_dtypes/django/api/findings_routes.py
+++ lx_dtypes/django/api/findings_routes.py
@@ -1,1186 +1,1190 @@
 from __future__ import annotations
 
 from functools import lru_cache
 from typing import (
     TYPE_CHECKING,
     Any,
     Callable,
     Dict,
     List,
     NoReturn,
     Optional,
     Protocol,
     Set,
     TypeVar,
     cast,
 )
 
 from django.core.exceptions import ValidationError
 from django.db import IntegrityError, transaction
 from django.db.models import QuerySet
 from django.utils import timezone
 from pydantic import BaseModel, Field
 
 from lx_dtypes.models.ledger.p_examination.Pydantic import PExamination
 
 from .request_types import BaseRequest
 
 F = TypeVar("F", bound=Callable[..., Any])
 
 if TYPE_CHECKING:
     Schema = BaseModel
 else:
     from ninja import Schema
 
 
 class _RouteDecorator(Protocol):
     def __call__(self, func: F, /) -> F: ...
 
 
 class _TypedApi(Protocol):
     def get(self, path: str, /) -> _RouteDecorator: ...
 
     def post(self, path: str, /) -> _RouteDecorator: ...
 
     def patch(self, path: str, /) -> _RouteDecorator: ...
 
     def delete(self, path: str, /) -> _RouteDecorator: ...
 
 
 class PatientFindingClassificationInput(Schema):
     classification: int
     choice: int
 
 
 class PatientFindingCreateRequest(Schema):
     patient_examination: int
     finding: int
     classifications: List[PatientFindingClassificationInput] = Field(
         default_factory=list
     )
 
 
 class PatientFindingUpdateRequest(Schema):
     finding: Optional[int] = None
     is_active: Optional[bool] = None
     classifications: Optional[List[PatientFindingClassificationInput]] = None
 
 
 class PatientFindingClassificationsRequest(Schema):
     classifications: List[PatientFindingClassificationInput] = Field(
         default_factory=list
     )
     replace: bool = True
 
 
 _LOAD_MODULE_KB: Callable[..., Any] | None = None
 
 
 def clear_findings_route_caches() -> None:
     _kb_core_concepts_by_identity.cache_clear()
     _kb_lookup_by_identity.cache_clear()
 
 
 def _set_load_module_kb(load_module_kb: Callable[..., Any]) -> None:
     global _LOAD_MODULE_KB
     _LOAD_MODULE_KB = load_module_kb
 
 
 def _require_load_module_kb() -> Callable[..., Any]:
     if _LOAD_MODULE_KB is None:
         raise RuntimeError("findings routes are not initialized with a KB loader")
     return _LOAD_MODULE_KB
 
 
 def _runtime_descriptor_payloads_from_mapping(
     value: object, *, parent_choice_ref: str
 ) -> list[dict[str, object]]:
     if not isinstance(value, dict):
         return []
 
     payloads: list[dict[str, object]] = []
     for descriptor_name, descriptor_value in value.items():
         normalized_name = str(descriptor_name or "").strip()
         if not normalized_name:
             continue
         if not isinstance(descriptor_value, (str, int, float, bool, list)):
             continue
         payloads.append(
             {
                 "classification_choice_descriptor": normalized_name,
                 "descriptor_value": descriptor_value,
                 "patient_finding_classification_choice": parent_choice_ref,
             }
         )
     return payloads
 
 
 def _as_str_list_from_relation(relation: object) -> list[str]:
     if relation is None:
         return []
     if hasattr(relation, "all"):
         return [str(getattr(item, "pk", item)) for item in relation.all()]  # type: ignore[misc]
     if isinstance(relation, list):
         return [str(item) for item in relation]
     return [str(relation)]
 
 
 def _findings_module_name() -> str:
     from .terminology_routes import active_terminology_selection
 
     active = active_terminology_selection()
     if active is None:
         raise RuntimeError("No active knowledge-base bundle is selected.")
     return active[0]
 
 
 def _resolve_exam_kb_identity(patient_examination: Any) -> tuple[str, str | None]:
     module_name = str(
         getattr(patient_examination, "knowledge_base_module", "") or ""
     ).strip()
     version = str(
         getattr(patient_examination, "knowledge_base_version", "") or ""
     ).strip()
     if module_name:
         return module_name, version or None
 
     from .terminology_routes import active_terminology_selection
 
     active = active_terminology_selection()
     if active is None:
         raise RuntimeError("No active knowledge-base bundle is selected.")
     return active[0], active[1]
 
 
 def _resolve_catalog_kb_identity(
     module_name: str | None,
     module_version: str | None,
     orm_models: Callable[[], Dict[str, Any]],
     patient_examination_id: int | None,
     api_error: Callable[[int, str, str], NoReturn],
 ) -> tuple[str, str | None]:
     requested_module_name = str(module_name or "").strip()
     if requested_module_name:
         return requested_module_name, str(module_version or "").strip() or None
 
     if patient_examination_id is not None:
         patient_examination_model = orm_models()["PatientExamination"]
         patient_examination = patient_examination_model.objects.filter(
             id=patient_examination_id
         ).first()
         if not patient_examination:
             api_error(
                 404,
                 "not-found",
                 f"PatientExamination '{patient_examination_id}' not found.",
             )
         assert patient_examination is not None
         return _resolve_exam_kb_identity(patient_examination)
 
     from .terminology_routes import active_terminology_selection
 
     active = active_terminology_selection()
     if active is None:
         raise RuntimeError("No active knowledge-base bundle is selected.")
     return active
 
 
 def _norm_name(value: Optional[str]) -> str:
     return str(value or "").strip().lower().replace("-", "_").replace(" ", "_")
 
 
 @lru_cache(maxsize=8)
 def _kb_core_concepts_by_identity(
     module_name: str,
     version: str,
 ) -> Dict[str, Any]:
     loader = _require_load_module_kb()
     return cast(
         Dict[str, Any],
         loader(module_name, version=version).export_core_concepts(),
     )
 
 
 def _build_kb_lookup(core: Dict[str, Any]) -> Dict[str, Dict[str, Dict[str, Any]]]:
     examination_by_name = {
         _norm_name(item.get("name")): item for item in core.get("examination", [])
     }
     finding_by_name = {
         _norm_name(item.get("name")): item for item in core.get("finding", [])
     }
     classification_by_name = {
         _norm_name(item.get("name")): item for item in core.get("classification", [])
     }
     choice_by_name = {
         _norm_name(item.get("name")): item
         for item in core.get("classification_choice", [])
     }
+    indication_by_name = {
+        _norm_name(item.get("name")): item for item in core.get("indication", [])
+    }
     return {
         "examination": examination_by_name,
         "finding": finding_by_name,
         "classification": classification_by_name,
         "classification_choice": choice_by_name,
+        "indication": indication_by_name,
     }
 
 
 def _kb_core_concepts(module_name: str) -> Dict[str, Any]:
     loader = _require_load_module_kb()
     kb = loader(module_name)
     version = str(getattr(getattr(kb, "config", None), "version", "") or "").strip()
     if not version:
         return cast(Dict[str, Any], kb.export_core_concepts())
     return _kb_core_concepts_by_identity(module_name, version)
 
 
 @lru_cache(maxsize=8)
 def _kb_lookup_by_identity(
     module_name: str,
     version: str,
 ) -> Dict[str, Dict[str, Dict[str, Any]]]:
     core = _kb_core_concepts_by_identity(module_name, version)
     return _build_kb_lookup(core)
 
 
 def _kb_lookup(
     module_name: str, version: str | None = None
 ) -> Dict[str, Dict[str, Dict[str, Any]]]:
     if version:
         return _kb_lookup_by_identity(module_name, version)
 
     loader = _require_load_module_kb()
     kb = loader(module_name)
     version = str(getattr(getattr(kb, "config", None), "version", "") or "").strip()
     if not version:
         return _build_kb_lookup(cast(Dict[str, Any], kb.export_core_concepts()))
     return _kb_lookup_by_identity(module_name, version)
 
 
 def _active_patient_findings_queryset(
     orm_models: Callable[[], Dict[str, Any]],
 ) -> QuerySet[Any]:
     patient_finding_model = orm_models()["PatientFinding"]
     return cast(
         QuerySet[Any],
         patient_finding_model.objects.filter(is_active=True).select_related(
             "patient_examination", "finding"
         ),
     )
 
 
 def build_p_examination_payload_from_host_ledger(
     patient_examination: object,
     *,
     route_module_name: str,
     orm_models: Callable[[], Dict[str, Any]],
     active_patient_findings_queryset: Callable[[], Any] | None = None,
 ) -> PExamination:
     patient_examination_id = getattr(patient_examination, "id", None)
     if patient_examination_id is None:
         raise ValueError("PatientExamination is missing an id.")
 
     examination_obj = getattr(patient_examination, "examination_safe", None) or getattr(
         patient_examination, "examination", None
     )
     examination_name = str(getattr(examination_obj, "name", "") or "").strip()
     if not examination_name:
         raise ValueError(
             f"PatientExamination '{patient_examination_id}' is missing examination name."
         )
 
     patient_value = getattr(patient_examination, "patient_id", None)
     if patient_value is None:
         patient_obj = getattr(patient_examination, "patient", None)
         patient_value = getattr(patient_obj, "pk", None)
     patient_token = str(
         patient_value or f"patient_examination_{patient_examination_id}"
     )
 
     module_from_ledger = str(
         getattr(patient_examination, "knowledge_base_module", "") or ""
     ).strip()
     version_from_ledger = str(
         getattr(patient_examination, "knowledge_base_version", "") or ""
     ).strip()
 
     queryset_provider = (
         active_patient_findings_queryset
         if active_patient_findings_queryset is not None
         else lambda: _active_patient_findings_queryset(orm_models)
     )
     patient_findings_qs = queryset_provider().filter(
         patient_examination_id=patient_examination_id
     )
     patient_findings_payload: list[dict[str, object]] = []
     for patient_finding in patient_findings_qs:
         finding_name = str(getattr(patient_finding.finding, "name", "") or "").strip()
         if not finding_name:
             continue
 
         classifications_payload: list[dict[str, object]] = []
         active_classifications = (
             patient_finding.classifications.filter(is_active=True)
             .select_related("classification", "classification_choice")
             .all()
         )
         for index, item in enumerate(active_classifications):
             classification_name = str(
                 getattr(item.classification, "name", "") or ""
             ).strip()
             choice_name = str(
                 getattr(item.classification_choice, "name", "") or ""
             ).strip()
             if not classification_name:
                 continue
             if not choice_name:
                 choice_name = classification_name
 
             choice_ref = f"pe_{patient_examination_id}_pf_{patient_finding.id}_choice_{index + 1}"
             classifications_payload.append(
                 {
                     "classification": classification_name,
                     "classification_choice": choice_name,
                     "patient_finding_classifications": str(item.id),
                     "patient_finding_classification_choice_descriptors": (
                         _runtime_descriptor_payloads_from_mapping(
                             getattr(item, "numerical_descriptors", {}),
                             parent_choice_ref=choice_ref,
                         )
                     ),
                 }
             )
 
         interventions_payload: list[dict[str, object]] = []
         active_interventions = (
             patient_finding.interventions.filter(is_active=True)
             .select_related("intervention")
             .all()
         )
         for item in active_interventions:
             intervention_name = str(
                 getattr(item.intervention, "name", "") or ""
             ).strip()
             if not intervention_name:
                 continue
             interventions_payload.append(
                 {
                     "patient_finding_interventions": str(item.id),
                     "intervention": intervention_name,
                 }
             )
 
         patient_findings_payload.append(
             {
                 "finding": finding_name,
                 "patient_examination": str(patient_examination_id),
                 "patient_finding_classifications": [
                     {
                         "patient_finding": str(patient_finding.id),
                         "patient_finding_classification_choices": classifications_payload,
                     }
                 ]
                 if classifications_payload
                 else [],
                 "patient_finding_interventions": [
                     {
                         "patient_finding": str(patient_finding.id),
                         "patient_finding_interventions": interventions_payload,
                     }
                 ]
                 if interventions_payload
                 else [],
             }
         )
 
     payload = {
         "patient": patient_token,
         "examiners": _as_str_list_from_relation(
             getattr(patient_examination, "examiners", None)
         ),
         "examination": examination_name,
         "knowledge_base_module": module_from_ledger or route_module_name,
         "knowledge_base_version": version_from_ledger or None,
         "patient_findings": patient_findings_payload,
     }
     return PExamination.model_validate(payload)
 
 
 def _serialize_choice(choice: Any) -> Dict[str, Any]:
     return {
         "id": choice.id,
         "name": choice.name,
         "description": choice.description,
         "subcategories": choice.subcategories,
         "numerical_descriptors": choice.numerical_descriptors,
     }
 
 
 def _serialize_classification(
     classification: Any, *, required: bool = False
 ) -> Dict[str, Any]:
     choices = classification.choices.all()
     classification_types = [
         _norm_name(c_type.name) for c_type in classification.classification_types.all()
     ]
     return {
         "id": classification.id,
         "name": classification.name,
         "description": classification.description,
         "required": required,
         "classification_types": classification_types,
         "choices": [_serialize_choice(choice) for choice in choices],
     }
 
 
 def _split_classifications(
     classifications: List[Dict[str, Any]],
 ) -> Dict[str, List[Dict[str, Any]]]:
     location: List[Dict[str, Any]] = []
     morphology: List[Dict[str, Any]] = []
     for classification in classifications:
         c_types = {
             _norm_name(v) for v in classification.get("classification_types", [])
         }
         if "location" in c_types:
             location.append(classification)
         if "morphology" in c_types:
             morphology.append(classification)
     return {
         "location_classifications": location,
         "morphology_classifications": morphology,
     }
 
 
 def _serialize_finding(
     finding: Any,
     *,
     allowed_classification_names: Optional[Set[str]] = None,
     required_classification_names: Optional[Set[str]] = None,
 ) -> Dict[str, Any]:
     all_classifications = finding.finding_classifications.all().prefetch_related(
         "choices", "classification_types"
     )
     selected_classifications = []
     for classification in all_classifications:
         c_name = _norm_name(classification.name)
         if allowed_classification_names and c_name not in allowed_classification_names:
             continue
         selected_classifications.append(
             _serialize_classification(
                 classification,
                 required=(
                     required_classification_names is not None
                     and c_name in required_classification_names
                 ),
             )
         )
     split = _split_classifications(selected_classifications)
     return {
         "id": finding.id,
         "name": finding.name,
         "description": finding.description,
         "classifications": selected_classifications,
         "location_classifications": split["location_classifications"],
         "morphology_classifications": split["morphology_classifications"],
         "FindingClassifications": selected_classifications,
     }
 
 
 def _serialize_patient_finding_classification(
     item: Any,
 ) -> Dict[str, Any]:
     return {
         "id": item.id,
         "classification": item.classification_id,
         "classification_choice": item.classification_choice_id,
         "classification_name": item.classification.name,
         "classification_choice_name": item.classification_choice.name,
         "subcategories": item.subcategories,
         "numerical_descriptors": item.numerical_descriptors,
         "is_active": item.is_active,
     }
 
 
 def _serialize_patient_finding(item: Any) -> Dict[str, Any]:
     classifications = item.classifications.filter(is_active=True).select_related(
         "classification", "classification_choice"
     )
     return {
         "id": item.id,
         "patient_examination": item.patient_examination_id,
         "finding": item.finding_id,
         "is_active": item.is_active,
         "created_at": item.created_at.isoformat() if item.created_at else None,
         "updated_at": item.updated_at.isoformat() if item.updated_at else None,
         "classifications": [
             _serialize_patient_finding_classification(classification)
             for classification in classifications
         ],
     }
 
 
 def _resolve_exam_kb_finding_names(
     examination: Any, *, module_name: str, version: str | None = None
 ) -> Optional[Set[str]]:
     lookup = _kb_lookup(module_name, version=version)
     exam_entry = lookup["examination"].get(_norm_name(examination.name))
     if not exam_entry:
         return None
     finding_names = exam_entry.get("findings", [])
     if not isinstance(finding_names, list):
         return None
     return {_norm_name(name) for name in finding_names}
 
 
 def _resolve_kb_finding_classification_names(
     finding: Any, *, module_name: str, version: str | None = None
 ) -> Optional[Set[str]]:
     lookup = _kb_lookup(module_name, version=version)
     finding_entry = lookup["finding"].get(_norm_name(finding.name))
     if not finding_entry:
         return None
     classifications = finding_entry.get("classifications", [])
     if not isinstance(classifications, list):
         return None
     return {_norm_name(name) for name in classifications}
 
 
 def _resolve_kb_classification_choice_names(
     classification: Any, *, module_name: str, version: str | None = None
 ) -> Optional[Set[str]]:
     lookup = _kb_lookup(module_name, version=version)
     classification_entry = lookup["classification"].get(_norm_name(classification.name))
     if not classification_entry:
         return None
     choices = classification_entry.get("classification_choices", [])
     if not isinstance(choices, list):
         return None
     return {_norm_name(name) for name in choices}
 
 
 def _validate_finding_for_examination(
     finding: Any,
     patient_examination: Any,
     *,
     module_name: str,
     version: str | None = None,
     api_error: Callable[[int, str, str], NoReturn],
 ) -> None:
     available_findings = patient_examination.examination_safe.get_available_findings()
     if finding not in available_findings:
         api_error(
             400,
             "invalid-finding",
             f"Finding '{finding.name}' is not allowed for examination '{patient_examination.examination_safe.name}'.",
         )
 
     kb_allowed_names = _resolve_exam_kb_finding_names(
         patient_examination.examination_safe, module_name=module_name, version=version
     )
     if (
         kb_allowed_names is not None
         and _norm_name(finding.name) not in kb_allowed_names
     ):
         api_error(
             400,
             "invalid-finding",
             f"Finding '{finding.name}' is not present in dtypes module '{module_name}' for examination '{patient_examination.examination_safe.name}'.",
         )
 
 
 def _validate_classification_payload(
     *,
     finding: Any,
     classification: Any,
     choice: Any,
     module_name: str,
     version: str | None = None,
     api_error: Callable[[int, str, str], NoReturn],
 ) -> None:
     if not finding.finding_classifications.filter(id=classification.id).exists():
         api_error(
             400,
             "invalid-choice",
             f"Classification '{classification.name}' is not valid for finding '{finding.name}'.",
         )
     if not classification.choices.filter(id=choice.id).exists():
         api_error(
             400,
             "invalid-choice",
             f"Choice '{choice.name}' is not valid for classification '{classification.name}'.",
         )
 
     kb_classifications = _resolve_kb_finding_classification_names(
         finding, module_name=module_name, version=version
     )
     if (
         kb_classifications is not None
         and _norm_name(classification.name) not in kb_classifications
     ):
         api_error(
             400,
             "invalid-choice",
             f"Classification '{classification.name}' is not defined in dtypes for finding '{finding.name}'.",
         )
 
     kb_choices = _resolve_kb_classification_choice_names(
         classification, module_name=module_name, version=version
     )
     if kb_choices is not None and _norm_name(choice.name) not in kb_choices:
         api_error(
             400,
             "invalid-choice",
             f"Choice '{choice.name}' is not defined in dtypes for classification '{classification.name}'.",
         )
 
 
 def _replace_patient_finding_classifications(
     patient_finding: Any,
     entries: List[PatientFindingClassificationInput],
     *,
     module_name: str,
     version: str | None = None,
     orm_models: Callable[[], Dict[str, Any]],
     api_error: Callable[[int, str, str], NoReturn],
 ) -> None:
     patient_finding.classifications.all().delete()
     finding_classification_model = orm_models()["FindingClassification"]
     finding_classification_choice_model = orm_models()["FindingClassificationChoice"]
     patient_finding_classification_model = orm_models()["PatientFindingClassification"]
     for entry in entries:
         classification = finding_classification_model.objects.filter(
             id=entry.classification
         ).first()
         if not classification:
             api_error(
                 400,
                 "invalid-choice",
                 f"Classification id '{entry.classification}' does not exist.",
             )
         choice = finding_classification_choice_model.objects.filter(
             id=entry.choice
         ).first()
         if not choice:
             api_error(
                 400,
                 "invalid-choice",
                 f"Classification choice id '{entry.choice}' does not exist.",
             )
         assert classification is not None
         assert choice is not None
         _validate_classification_payload(
             finding=patient_finding.finding,
             classification=classification,
             choice=choice,
             module_name=module_name,
             version=version,
             api_error=api_error,
         )
         patient_finding_classification_model.objects.create(
             finding=patient_finding,
             classification=classification,
             classification_choice=choice,
             is_active=True,
         )
 
 
 def _get_or_create_active_patient_finding_classification(
     patient_finding: Any,
     *,
     classification: Any,
     choice: Any,
     orm_models: Callable[[], Dict[str, Any]],
 ) -> Any:
     existing = patient_finding.classifications.filter(
         classification=classification,
         classification_choice=choice,
         is_active=True,
     ).first()
     if existing is not None:
         return existing
     patient_finding_classification_model = orm_models()["PatientFindingClassification"]
     return patient_finding_classification_model.objects.create(
         finding=patient_finding,
         classification=classification,
         classification_choice=choice,
         is_active=True,
     )
 
 
 def register_findings_routes(
     api: _TypedApi,
     *,
     load_module_kb: Callable[..., Any],
     orm_models: Callable[[], Dict[str, Any]],
     api_error: Callable[[int, str, str], NoReturn],
     authenticate_request_user: Callable[[BaseRequest], Any | None],
     patient_examination_access_allowed: Callable[[BaseRequest, object], bool],
     patient_finding_access_allowed: Callable[[BaseRequest, object], bool],
     patient_findings_queryset_for_request: Callable[[BaseRequest], Any],
     build_p_examination_payload_from_host_ledger: Callable[..., PExamination]
     | None = None,
     persist_patient_examination_dtypes_record: Callable[
         [object, PExamination], dict[str, Any]
     ]
     | None = None,
 ) -> None:
     _set_load_module_kb(load_module_kb)
 
     def require_authenticated_actor(request: BaseRequest) -> Any:
         actor = authenticate_request_user(request)
         if actor is None:
             api_error(401, "authentication-required", "Authentication is required.")
         return actor
 
     def require_patient_finding_access(
         request: BaseRequest, patient_finding: object, patient_finding_id: int
     ) -> None:
         if not patient_finding_access_allowed(request, patient_finding):
             api_error(
                 404,
                 "not-found",
                 f"Patient finding '{patient_finding_id}' not found.",
             )
 
     def resolve_findings_module_name() -> str:
         try:
             return _findings_module_name()
         except RuntimeError as exc:
             api_error(409, "no-active-knowledge-base", str(exc))
 
     def refresh_patient_examination_dtypes_record(patient_examination: object) -> None:
         if (
             build_p_examination_payload_from_host_ledger is None
             or persist_patient_examination_dtypes_record is None
         ):
             return
         module_name = resolve_findings_module_name()
         try:
             module_name_for_record, _ = _resolve_exam_kb_identity(patient_examination)
         except RuntimeError:
             module_name_for_record = module_name
         payload = build_p_examination_payload_from_host_ledger(
             patient_examination,
             route_module_name=module_name_for_record,
         )
         persist_patient_examination_dtypes_record(patient_examination, payload)
 
     @api.get("/core-concepts/{module_name}")
     def core_concepts_by_module(
         request: BaseRequest, module_name: str
     ) -> Dict[str, Any]:
         """
         Return canonical core concept payloads for one KB module.
         """
         del request
         kb = load_module_kb(module_name)
         payload = cast(Dict[str, Any], kb.export_core_concepts())
         config = getattr(kb, "config", None)
         payload["knowledge_base_module"] = str(
             getattr(config, "name", module_name) or module_name
         ).strip()
         payload["knowledge_base_version"] = (
             str(getattr(config, "version", "") or "").strip() or None
         )
         return payload
 
     @api.get("/examinations/{examination_id}/findings/")
     def findings_by_examination(
         request: BaseRequest,
         examination_id: int,
         module_name: Optional[str] = None,
         module_version: Optional[str] = None,
         patient_examination_id: Optional[int] = None,
     ) -> List[Dict[str, Any]]:
         del request
         try:
             module_name, resolved_version = _resolve_catalog_kb_identity(
                 module_name=module_name,
                 module_version=module_version,
                 orm_models=orm_models,
                 patient_examination_id=patient_examination_id,
                 api_error=api_error,
             )
         except RuntimeError as exc:
             api_error(409, "no-active-knowledge-base", str(exc))
         examination_model = orm_models()["Examination"]
         examination = examination_model.objects.filter(id=examination_id).first()
         if not examination:
             api_error(404, "not-found", f"Examination '{examination_id}' not found.")
 
         assert examination is not None
         if patient_examination_id is not None:
             patient_examination_model = orm_models()["PatientExamination"]
             patient_examination = patient_examination_model.objects.filter(
                 id=patient_examination_id
             ).first()
             if not patient_examination:
                 api_error(
                     404,
                     "not-found",
                     f"PatientExamination '{patient_examination_id}' not found.",
                 )
             assert patient_examination is not None
             if patient_examination.examination_id != examination.id:
                 api_error(
                     404,
                     "not-found",
                     "Patient examination "
                     f"'{patient_examination_id}' does not belong to "
                     f"examination '{examination_id}'.",
                 )
         findings = list(examination.get_available_findings())
         kb_allowed_finding_names = _resolve_exam_kb_finding_names(
             examination, module_name=module_name, version=resolved_version
         )
         if kb_allowed_finding_names is not None:
             findings = [
                 finding
                 for finding in findings
                 if _norm_name(finding.name) in kb_allowed_finding_names
             ]
 
         response = []
         for finding in findings:
             kb_allowed_classifications = _resolve_kb_finding_classification_names(
                 finding, module_name=module_name, version=resolved_version
             )
             response.append(
                 _serialize_finding(
                     finding,
                     allowed_classification_names=kb_allowed_classifications,
                     required_classification_names=set(),
                 )
             )
         return response
 
     @api.get("/findings/{finding_id}/classifications/")
     def classifications_by_finding(
         request: BaseRequest,
         finding_id: int,
         module_name: Optional[str] = None,
         module_version: Optional[str] = None,
         patient_examination_id: Optional[int] = None,
     ) -> List[Dict[str, Any]]:
         del request
         try:
             module_name, resolved_version = _resolve_catalog_kb_identity(
                 module_name=module_name,
                 module_version=module_version,
                 orm_models=orm_models,
                 patient_examination_id=patient_examination_id,
                 api_error=api_error,
             )
         except RuntimeError as exc:
             api_error(409, "no-active-knowledge-base", str(exc))
         finding_model = orm_models()["Finding"]
         finding = finding_model.objects.filter(id=finding_id).first()
         if not finding:
             api_error(404, "not-found", f"Finding '{finding_id}' not found.")
         assert finding is not None
 
         kb_allowed_classifications = _resolve_kb_finding_classification_names(
             finding, module_name=module_name, version=resolved_version
         )
         serialized = _serialize_finding(
             finding,
             allowed_classification_names=kb_allowed_classifications,
             required_classification_names=set(),
         )
         return cast(List[Dict[str, Any]], serialized["classifications"])
 
     @api.get("/classifications/{classification_id}/choices/")
     def choices_by_classification(
         request: BaseRequest,
         classification_id: int,
         module_name: Optional[str] = None,
         module_version: Optional[str] = None,
         patient_examination_id: Optional[int] = None,
     ) -> Dict[str, Any]:
         del request
         try:
             module_name, resolved_version = _resolve_catalog_kb_identity(
                 module_name=module_name,
                 module_version=module_version,
                 orm_models=orm_models,
                 patient_examination_id=patient_examination_id,
                 api_error=api_error,
             )
         except RuntimeError as exc:
             api_error(409, "no-active-knowledge-base", str(exc))
         finding_classification_model = orm_models()["FindingClassification"]
         classification = finding_classification_model.objects.filter(
             id=classification_id
         ).first()
         if not classification:
             api_error(
                 404, "not-found", f"Classification '{classification_id}' not found."
             )
         assert classification is not None
 
         kb_allowed_choices = _resolve_kb_classification_choice_names(
             classification, module_name=module_name, version=resolved_version
         )
         all_choices = list(classification.choices.all())
         if kb_allowed_choices is not None:
             all_choices = [
                 choice
                 for choice in all_choices
                 if _norm_name(choice.name) in kb_allowed_choices
             ]
         return {"choices": [_serialize_choice(choice) for choice in all_choices]}
 
     @api.get("/patient-findings/")
     def list_patient_findings(
         request: BaseRequest, patient_examination: Optional[int] = None
     ) -> List[Dict[str, Any]]:
         require_authenticated_actor(request)
         queryset = patient_findings_queryset_for_request(request)
         if patient_examination is not None:
             queryset = queryset.filter(patient_examination_id=patient_examination)
         return [_serialize_patient_finding(item) for item in queryset]
 
     @api.post("/patient-findings/")
     def create_patient_finding(
         request: BaseRequest, payload: PatientFindingCreateRequest
     ) -> Dict[str, Any]:
         require_authenticated_actor(request)
         patient_examination_model = orm_models()["PatientExamination"]
         finding_model = orm_models()["Finding"]
         patient_finding_model = orm_models()["PatientFinding"]
         patient_examination = patient_examination_model.objects.filter(
             id=payload.patient_examination
         ).first()
         if not patient_examination:
             api_error(
                 404,
                 "not-found",
                 f"PatientExamination '{payload.patient_examination}' not found.",
             )
         assert patient_examination is not None
         if not patient_examination_access_allowed(request, patient_examination):
             api_error(
                 404,
                 "not-found",
                 f"PatientExamination '{payload.patient_examination}' not found.",
             )
         module_name, module_version = _resolve_exam_kb_identity(patient_examination)
 
         finding = finding_model.objects.filter(id=payload.finding).first()
         if not finding:
             api_error(404, "not-found", f"Finding '{payload.finding}' not found.")
         assert finding is not None
 
         _validate_finding_for_examination(
             finding=finding,
             patient_examination=patient_examination,
             module_name=module_name,
             version=module_version,
             api_error=api_error,
         )
 
         try:
             with transaction.atomic():
                 patient_finding = patient_finding_model.objects.create(
                     patient_examination=patient_examination,
                     finding=finding,
                 )
                 if payload.classifications:
                     _replace_patient_finding_classifications(
                         patient_finding,
                         payload.classifications,
                         module_name=module_name,
                         version=module_version,
                         orm_models=orm_models,
                         api_error=api_error,
                     )
                 refresh_patient_examination_dtypes_record(patient_examination)
                 return _serialize_patient_finding(patient_finding)
         except IntegrityError as exc:
             if "unique_active_finding_per_examination" in str(exc):
                 api_error(
                     400,
                     "duplicate-finding",
                     f"Finding '{finding.name}' is already active for this patient examination.",
                 )
             raise
         except ValidationError as exc:
             message = str(exc)
             normalized_message = message.lower()
             if "erforderliche findings fehlen" in normalized_message:
                 code = "required-finding"
             elif (
                 "unique_active_finding_per_examination" in normalized_message
                 or "already exists" in normalized_message
                 or "bereits" in normalized_message
             ):
                 code = "duplicate-finding"
             else:
                 code = "invalid-finding"
             api_error(400, code, message)
 
     @api.patch("/patient-findings/{patient_finding_id}/")
     def patch_patient_finding(
         request: BaseRequest,
         patient_finding_id: int,
         payload: PatientFindingUpdateRequest,
     ) -> Dict[str, Any]:
         actor = require_authenticated_actor(request)
         patient_finding = (
             patient_findings_queryset_for_request(request)
             .filter(id=patient_finding_id)
             .first()
         )
         if not patient_finding:
             api_error(
                 404, "not-found", f"Patient finding '{patient_finding_id}' not found."
             )
         assert patient_finding is not None
         require_patient_finding_access(request, patient_finding, patient_finding_id)
         module_name, module_version = _resolve_exam_kb_identity(
             patient_finding.patient_examination
         )
 
         with transaction.atomic():
             if payload.finding is not None:
                 finding_model = orm_models()["Finding"]
                 finding = finding_model.objects.filter(id=payload.finding).first()
                 if not finding:
                     api_error(
                         404, "not-found", f"Finding '{payload.finding}' not found."
                     )
                 assert finding is not None
                 _validate_finding_for_examination(
                     finding=finding,
                     patient_examination=patient_finding.patient_examination,
                     module_name=module_name,
                     version=module_version,
                     api_error=api_error,
                 )
                 patient_finding.finding = finding
 
             if payload.is_active is not None:
                 if payload.is_active:
                     patient_finding.is_active = True
                     patient_finding.deactivated_at = None
                     patient_finding.deactivated_by = None
                 else:
                     patient_finding.is_active = False
                     patient_finding.deactivated_by = actor
                     patient_finding.deactivated_at = timezone.now()
 
             patient_finding.save()
 
             if payload.classifications is not None:
                 _replace_patient_finding_classifications(
                     patient_finding,
                     payload.classifications,
                     module_name=module_name,
                     version=module_version,
                     orm_models=orm_models,
                     api_error=api_error,
                 )
             refresh_patient_examination_dtypes_record(
                 patient_finding.patient_examination
             )
 
         return _serialize_patient_finding(patient_finding)
 
     @api.delete("/patient-findings/{patient_finding_id}/")
     def delete_patient_finding(
         request: BaseRequest, patient_finding_id: int
     ) -> Dict[str, Any]:
         actor = require_authenticated_actor(request)
 
         patient_finding = (
             patient_findings_queryset_for_request(request)
             .filter(id=patient_finding_id)
             .first()
         )
         if not patient_finding:
             api_error(
                 404, "not-found", f"Patient finding '{patient_finding_id}' not found."
             )
         assert patient_finding is not None
         require_patient_finding_access(request, patient_finding, patient_finding_id)
 
         with transaction.atomic():
             patient_finding.is_active = False
             patient_finding.deactivated_by = actor
             patient_finding.deactivated_at = timezone.now()
             patient_finding.save(
                 update_fields=["is_active", "deactivated_at", "deactivated_by"]
             )
             refresh_patient_examination_dtypes_record(
                 patient_finding.patient_examination
             )
         return {"success": True, "id": patient_finding_id}
 
     @api.post("/patient-findings/{patient_finding_id}/classifications/")
     def set_patient_finding_classifications(
         request: BaseRequest,
         patient_finding_id: int,
         payload: PatientFindingClassificationsRequest,
     ) -> Dict[str, Any]:
         require_authenticated_actor(request)
         patient_finding = (
             patient_findings_queryset_for_request(request)
             .filter(id=patient_finding_id)
             .first()
         )
         if not patient_finding:
             api_error(
                 404, "not-found", f"Patient finding '{patient_finding_id}' not found."
             )
         assert patient_finding is not None
         require_patient_finding_access(request, patient_finding, patient_finding_id)
         module_name, module_version = _resolve_exam_kb_identity(
             patient_finding.patient_examination
         )
 
         with transaction.atomic():
             finding_classification_model = orm_models()["FindingClassification"]
             finding_classification_choice_model = orm_models()[
                 "FindingClassificationChoice"
             ]
             if payload.replace:
                 patient_finding.classifications.all().delete()
             for entry in payload.classifications:
                 classification = finding_classification_model.objects.filter(
                     id=entry.classification
                 ).first()
                 if not classification:
                     api_error(
                         400,
                         "invalid-choice",
                         f"Classification id '{entry.classification}' does not exist.",
                     )
                 choice = finding_classification_choice_model.objects.filter(
                     id=entry.choice
                 ).first()
                 if not choice:
                     api_error(
                         400,
                         "invalid-choice",
                         f"Classification choice id '{entry.choice}' does not exist.",
                     )
                 assert classification is not None
                 assert choice is not None
                 _validate_classification_payload(
                     finding=patient_finding.finding,
                     classification=classification,
                     choice=choice,
                     module_name=module_name,
                     version=module_version,
                     api_error=api_error,
                 )
                 _get_or_create_active_patient_finding_classification(
                     patient_finding,
                     classification=classification,
                     choice=choice,
                     orm_models=orm_models,
                 )
             refresh_patient_examination_dtypes_record(
                 patient_finding.patient_examination
             )
 
         return _serialize_patient_finding(patient_finding)
diff --git lx_dtypes/django/api/main.py lx_dtypes/django/api/main.py
index ff4336b..507472c 100644
--- lx_dtypes/django/api/main.py
+++ lx_dtypes/django/api/main.py
@@ -1,697 +1,704 @@
 from __future__ import annotations
 
 import os
 from functools import lru_cache
 from importlib import import_module
 from typing import (
     TYPE_CHECKING,
     Any,
     Callable,
     Dict,
     List,
     NoReturn,
     Optional,
     Protocol,
     Set,
     TypedDict,
     TypeVar,
     cast,
     Literal,
     runtime_checkable,
 )
 
 from django.conf import settings
 from ninja.errors import HttpError  # type: ignore[import-untyped]
 
 from lx_dtypes.models.contracts import KnowledgeBaseContract
 from lx_dtypes.models.interface.KnowledgeBaseResolver import (
     KnowledgeBaseVersionNotFoundError,
     clear_knowledge_base_resolver_caches,
     load_knowledge_base,
 )
 from lx_dtypes.models.interface import KnowledgeBaseResolver as _knowledge_base_resolver
 from lx_dtypes.models.ledger.p_examination.Pydantic import PExamination
 
 from .findings_routes import (
     PatientFindingClassificationInput,
     build_p_examination_payload_from_host_ledger as _build_payload_from_host_ledger,
     clear_findings_route_caches,
     register_findings_routes,
 )
+from .indications_routes import register_indications_routes
 from .examinations_routes import register_examinations_routes
 from .request_types import BaseRequest
 from .report_template_routes import register_report_template_routes
 from .lookup_tracker import register_runtime_lookup_tracker
 from .terminology_routes import (
     active_terminology_selection,
     register_terminology_routes,
 )
 
 F = TypeVar("F", bound=Callable[..., Any])
 ReportLanguageCode = Literal["de", "en"]
 
 
 class ReportLanguageOption(TypedDict):
     code: ReportLanguageCode
     label: str
 
 
 class ReportLanguagesResponse(TypedDict):
     default_language: ReportLanguageCode
     languages: List[ReportLanguageOption]
 
 
 class _RouteDecorator(Protocol):
     def __call__(self, func: F, /) -> F: ...
 
 
 class _TypedApi(Protocol):
     @property
     def urls(self) -> Any: ...
 
     def get(self, path: str, /) -> _RouteDecorator: ...
 
     def post(self, path: str, /) -> _RouteDecorator: ...
 
     def patch(self, path: str, /) -> _RouteDecorator: ...
 
     def delete(self, path: str, /) -> _RouteDecorator: ...
 
     def exception_handler(self, exc_class: type[Exception], /) -> _RouteDecorator: ...
 
     def create_response(self, request: Any, data: Any, *, status: int) -> Any: ...
 
 
 if TYPE_CHECKING:
     api = cast(_TypedApi, object())
 else:
     from ninja import NinjaAPI
 
     api = cast(_TypedApi, NinjaAPI(urls_namespace="lx_dtypes_base_api"))
 
 
 @api.get("/reporting/languages")
 def reporting_languages(request: BaseRequest) -> ReportLanguagesResponse:
     """Return the report languages supported by LXDM concept labels."""
     del request
     return {
         "default_language": "de",
         "languages": [
             {"code": "de", "label": "Deutsch"},
             {"code": "en", "label": "English"},
         ],
     }
 
 
 @lru_cache(maxsize=1)
 def _host_models_module() -> Any:
     module_path = getattr(settings, "LX_DTYPES_HOST_MODELS_MODULE", None) or os.getenv(
         "LX_DTYPES_HOST_MODELS_MODULE"
     )
     if not module_path:
         raise RuntimeError(
             "LX_DTYPES_HOST_MODELS_MODULE must be configured to use lx_dtypes.django.api."
         )
     return import_module(module_path)
 
 
 def _host_integration_is_configured() -> bool:
     return bool(
         getattr(settings, "LX_DTYPES_HOST_MODELS_MODULE", None)
         or os.getenv("LX_DTYPES_HOST_MODELS_MODULE")
     )
 
 
 @lru_cache(maxsize=1)
 def _orm_models() -> Dict[str, Any]:
     host_models = _host_models_module()
     return {
         "Examination": getattr(host_models, "Examination"),
         "Finding": getattr(host_models, "Finding"),
         "FindingClassification": getattr(host_models, "FindingClassification"),
         "FindingClassificationChoice": getattr(
             host_models, "FindingClassificationChoice"
         ),
         "PatientExamination": getattr(host_models, "PatientExamination"),
         "PatientFinding": getattr(host_models, "PatientFinding"),
         "PatientFindingClassification": getattr(
             host_models, "PatientFindingClassification"
         ),
     }
 
 
 def _persist_patient_examination_dtypes_record(
     patient_examination: object,
     payload: PExamination,
 ) -> dict[str, Any]:
     persist = getattr(
         _host_models_module(), "persist_patient_examination_dtypes_record"
     )
     return cast(dict[str, Any], persist(patient_examination, payload))
 
 
 def _authenticate_request_user(request: BaseRequest) -> Any | None:
     if not _host_integration_is_configured():
         return None
     authenticate = getattr(_host_models_module(), "authenticate_request_user", None)
     if callable(authenticate):
         return authenticate(request)
     return _request_user_if_authenticated(request)
 
 
 def _patient_finding_access_allowed(
     request: BaseRequest, patient_finding: object
 ) -> bool:
     authorize = getattr(_host_models_module(), "patient_finding_access_allowed", None)
     if not callable(authorize):
         return False
     return bool(authorize(request, patient_finding))
 
 
 def _patient_examination_access_allowed(
     request: BaseRequest, patient_examination: object
 ) -> bool:
     authorize = getattr(
         _host_models_module(), "patient_examination_access_allowed", None
     )
     if not callable(authorize):
         return False
     return bool(authorize(request, patient_examination))
 
 
 def _patient_findings_queryset_for_request(request: BaseRequest) -> Any:
     scope_queryset = getattr(
         _host_models_module(), "patient_findings_queryset_for_request", None
     )
     if not callable(scope_queryset):
         return _active_patient_findings_queryset().none()
     return scope_queryset(request)
 
 
 def _terminology_write_access_allowed(actor: object) -> bool:
     authorize = getattr(_host_models_module(), "terminology_write_access_allowed", None)
     return bool(callable(authorize) and authorize(actor))
 
 
 def _report_template_access_allowed(
     actor: object,
     capability: Literal["report_template:read", "report_template:write"],
 ) -> bool:
     if not _host_integration_is_configured():
         return False
     authorize = getattr(_host_models_module(), "report_template_access_allowed", None)
     return bool(callable(authorize) and authorize(actor, capability))
 
 
 class StructuredApiError(Exception):
     def __init__(self, status_code: int, code: str, message: str) -> None:
         self.status_code = status_code
         self.code = code
         self.message = message
         super().__init__(message)
 
 
 @api.exception_handler(StructuredApiError)
 def handle_structured_api_error(request: Any, exc: StructuredApiError) -> Any:
     return api.create_response(
         request,
         {"code": exc.code, "message": exc.message},
         status=exc.status_code,
     )
 
 
 def _resolve_active_version(module_name: str, version: str | None) -> str | None:
     if version:
         return version
     active = active_terminology_selection()
     if active is not None and active[0] == module_name:
         return active[1]
     if active is None:
         raise HttpError(409, "No active knowledge-base bundle is selected.")
     raise HttpError(
         409,
         f"Knowledge-base module '{module_name}' is not the active registered bundle.",
     )
 
 
 def _load_module_kb(
     module_name: str, version: str | None = None
 ) -> KnowledgeBaseContract:
     loader = _kb_loader()
     resolved_version = (
         _resolve_active_version(module_name, version)
         if loader is _knowledge_base_resolver
         else version
     )
     try:
         if loader is _knowledge_base_resolver:
             loaded_kb = load_knowledge_base(module_name, version=resolved_version)
         else:
             try:
                 loaded_kb = loader.load_knowledge_base(
                     module_name, version=resolved_version
                 )
             except TypeError:
                 loaded_kb = loader.load_knowledge_base(module_name)
         kb = cast(
             KnowledgeBaseContract,
             loaded_kb,
         )
     except KnowledgeBaseVersionNotFoundError as exc:
         raise HttpError(
             409,
             "Requested knowledge-base version is not provisioned locally for "
             f"module '{module_name}' and version '{resolved_version}'.",
         ) from exc
     except ValueError as exc:
         raise HttpError(404, f"Unknown knowledge-base module '{module_name}'.") from exc
     register_runtime_lookup_tracker(cast(Any, kb))
     return kb
 
 
 def _kb_loader() -> Any:
     return _knowledge_base_resolver
 
 
 def _clear_kb_caches() -> None:
     clear_findings_route_caches()
     clear_knowledge_base_resolver_caches()
 
 
 def _resolve_payload_kb_identity(
     route_module_name: str,
     payload: PExamination,
 ) -> tuple[str, str | None]:
     payload_module_name = str(payload.knowledge_base_module or "").strip()
     payload_version = str(payload.knowledge_base_version or "").strip() or None
 
     if payload_module_name and payload_module_name != route_module_name:
         raise HttpError(
             409,
             "Payload knowledge-base module does not match route module: "
             f"'{payload_module_name}' != '{route_module_name}'.",
         )
 
     return payload_module_name or route_module_name, payload_version
 
 
 def _api_error(status: int, code: str, message: str) -> NoReturn:
     raise StructuredApiError(status, code, message)
 
 
 @runtime_checkable
 class _RelatedManagerLike(Protocol):
     def all(self) -> Any: ...
 
 
 def _as_str_list_from_relation(relation: object) -> list[str]:
     if relation is None:
         return []
     if isinstance(relation, _RelatedManagerLike):
         return [str(getattr(item, "pk", item)) for item in relation.all()]
     if isinstance(relation, list):
         return [str(item) for item in relation]
     return [str(relation)]
 
 
 def _active_patient_findings_queryset() -> Any:
     from .findings_routes import _active_patient_findings_queryset as _active_queryset
 
     return _active_queryset(lambda: _orm_models())
 
 
 def _build_p_examination_payload_from_host_ledger(
     patient_examination: object, *, route_module_name: str
 ) -> PExamination:
     return _build_payload_from_host_ledger(
         patient_examination,
         route_module_name=route_module_name,
         orm_models=lambda: _orm_models(),
         active_patient_findings_queryset=lambda: _active_patient_findings_queryset(),
     )
 
 
 def _norm_name(value: Optional[str]) -> str:
     return str(value or "").strip().lower().replace("-", "_").replace(" ", "_")
 
 
 @lru_cache(maxsize=8)
 def _kb_core_concepts(module_name: str) -> Dict[str, Any]:
     return _load_module_kb(module_name).export_core_concepts()
 
 
 @lru_cache(maxsize=8)
 def _kb_lookup(module_name: str) -> Dict[str, Dict[str, Dict[str, Any]]]:
     core = _kb_core_concepts(module_name)
     examination_by_name = {
         _norm_name(item.get("name")): item for item in core.get("examination", [])
     }
     finding_by_name = {
         _norm_name(item.get("name")): item for item in core.get("finding", [])
     }
     classification_by_name = {
         _norm_name(item.get("name")): item for item in core.get("classification", [])
     }
     choice_by_name = {
         _norm_name(item.get("name")): item
         for item in core.get("classification_choice", [])
     }
     return {
         "examination": examination_by_name,
         "finding": finding_by_name,
         "classification": classification_by_name,
         "classification_choice": choice_by_name,
     }
 
 
 def _request_user_if_authenticated(request: BaseRequest) -> Optional[Any]:
     user = getattr(request, "user", None)
     if getattr(user, "is_authenticated", False):
         return user
     return None
 
 
 def _serialize_choice(choice: Any) -> Dict[str, Any]:
     return {
         "id": choice.id,
         "name": choice.name,
         "description": choice.description,
         "subcategories": choice.subcategories,
         "numerical_descriptors": choice.numerical_descriptors,
     }
 
 
 def _serialize_classification(
     classification: Any, *, required: bool = False
 ) -> Dict[str, Any]:
     choices = classification.choices.all()
     classification_types = [
         _norm_name(c_type.name) for c_type in classification.classification_types.all()
     ]
     return {
         "id": classification.id,
         "name": classification.name,
         "description": classification.description,
         "required": required,
         "classification_types": classification_types,
         "choices": [_serialize_choice(choice) for choice in choices],
     }
 
 
 def _split_classifications(
     classifications: List[Dict[str, Any]],
 ) -> Dict[str, List[Dict[str, Any]]]:
     location: List[Dict[str, Any]] = []
     morphology: List[Dict[str, Any]] = []
     for classification in classifications:
         c_types = {
             _norm_name(v) for v in classification.get("classification_types", [])
         }
         if "location" in c_types:
             location.append(classification)
         if "morphology" in c_types:
             morphology.append(classification)
     return {
         "location_classifications": location,
         "morphology_classifications": morphology,
     }
 
 
 def _serialize_finding(
     finding: Any,
     *,
     allowed_classification_names: Optional[Set[str]] = None,
     required_classification_names: Optional[Set[str]] = None,
 ) -> Dict[str, Any]:
     all_classifications = finding.finding_classifications.all().prefetch_related(
         "choices", "classification_types"
     )
     selected_classifications = []
     for classification in all_classifications:
         c_name = _norm_name(classification.name)
         if allowed_classification_names and c_name not in allowed_classification_names:
             continue
         selected_classifications.append(
             _serialize_classification(
                 classification,
                 required=(
                     required_classification_names is not None
                     and c_name in required_classification_names
                 ),
             )
         )
     split = _split_classifications(selected_classifications)
     return {
         "id": finding.id,
         "name": finding.name,
         "description": finding.description,
         "classifications": selected_classifications,
         "location_classifications": split["location_classifications"],
         "morphology_classifications": split["morphology_classifications"],
         # Keep compatibility with legacy frontend field access:
         "FindingClassifications": selected_classifications,
     }
 
 
 def _serialize_patient_finding_classification(
     item: Any,
 ) -> Dict[str, Any]:
     return {
         "id": item.id,
         "classification": item.classification_id,
         "classification_choice": item.classification_choice_id,
         "classification_name": item.classification.name,
         "classification_choice_name": item.classification_choice.name,
         "subcategories": item.subcategories,
         "numerical_descriptors": item.numerical_descriptors,
         "is_active": item.is_active,
     }
 
 
 def _serialize_patient_finding(item: Any) -> Dict[str, Any]:
     classifications = item.classifications.filter(is_active=True).select_related(
         "classification", "classification_choice"
     )
     return {
         "id": item.id,
         "patient_examination": item.patient_examination_id,
         "finding": item.finding_id,
         "is_active": item.is_active,
         "created_at": item.created_at.isoformat() if item.created_at else None,
         "updated_at": item.updated_at.isoformat() if item.updated_at else None,
         "classifications": [
             _serialize_patient_finding_classification(classification)
             for classification in classifications
         ],
     }
 
 
 def _resolve_exam_kb_finding_names(
     examination: Any, *, module_name: str
 ) -> Optional[Set[str]]:
     lookup = _kb_lookup(module_name)
     exam_entry = lookup["examination"].get(_norm_name(examination.name))
     if not exam_entry:
         return None
     finding_names = exam_entry.get("findings", [])
     if not isinstance(finding_names, list):
         return None
     return {_norm_name(name) for name in finding_names}
 
 
 def _resolve_kb_finding_classification_names(
     finding: Any, *, module_name: str
 ) -> Optional[Set[str]]:
     lookup = _kb_lookup(module_name)
     finding_entry = lookup["finding"].get(_norm_name(finding.name))
     if not finding_entry:
         return None
     classifications = finding_entry.get("classifications", [])
     if not isinstance(classifications, list):
         return None
     return {_norm_name(name) for name in classifications}
 
 
 def _resolve_kb_classification_choice_names(
     classification: Any, *, module_name: str
 ) -> Optional[Set[str]]:
     lookup = _kb_lookup(module_name)
     classification_entry = lookup["classification"].get(_norm_name(classification.name))
     if not classification_entry:
         return None
     choices = classification_entry.get("classification_choices", [])
     if not isinstance(choices, list):
         return None
     return {_norm_name(name) for name in choices}
 
 
 def _validate_finding_for_examination(
     finding: Any,
     patient_examination: Any,
     *,
     module_name: str,
 ) -> None:
     available_findings = patient_examination.examination_safe.get_available_findings()
     if finding not in available_findings:
         _api_error(
             400,
             "invalid-finding",
             f"Finding '{finding.name}' is not allowed for examination '{patient_examination.examination_safe.name}'.",
         )
 
     kb_allowed_names = _resolve_exam_kb_finding_names(
         patient_examination.examination_safe, module_name=module_name
     )
     if (
         kb_allowed_names is not None
         and _norm_name(finding.name) not in kb_allowed_names
     ):
         _api_error(
             400,
             "invalid-finding",
             f"Finding '{finding.name}' is not present in dtypes module '{module_name}' for examination '{patient_examination.examination_safe.name}'.",
         )
 
 
 def _validate_classification_payload(
     *,
     finding: Any,
     classification: Any,
     choice: Any,
     module_name: str,
 ) -> None:
     if not finding.finding_classifications.filter(id=classification.id).exists():
         _api_error(
             400,
             "invalid-choice",
             f"Classification '{classification.name}' is not valid for finding '{finding.name}'.",
         )
     if not classification.choices.filter(id=choice.id).exists():
         _api_error(
             400,
             "invalid-choice",
             f"Choice '{choice.name}' is not valid for classification '{classification.name}'.",
         )
 
     kb_classifications = _resolve_kb_finding_classification_names(
         finding, module_name=module_name
     )
     if (
         kb_classifications is not None
         and _norm_name(classification.name) not in kb_classifications
     ):
         _api_error(
             400,
             "invalid-choice",
             f"Classification '{classification.name}' is not defined in dtypes for finding '{finding.name}'.",
         )
 
     kb_choices = _resolve_kb_classification_choice_names(
         classification, module_name=module_name
     )
     if kb_choices is not None and _norm_name(choice.name) not in kb_choices:
         _api_error(
             400,
             "invalid-choice",
             f"Choice '{choice.name}' is not defined in dtypes for classification '{classification.name}'.",
         )
 
 
 def _replace_patient_finding_classifications(
     patient_finding: Any,
     entries: List[PatientFindingClassificationInput],
     *,
     module_name: str,
 ) -> None:
     patient_finding.classifications.all().delete()
     finding_classification_model = _orm_models()["FindingClassification"]
     finding_classification_choice_model = _orm_models()["FindingClassificationChoice"]
     patient_finding_classification_model = _orm_models()["PatientFindingClassification"]
     for entry in entries:
         classification = finding_classification_model.objects.filter(
             id=entry.classification
         ).first()
         if not classification:
             _api_error(
                 400,
                 "invalid-choice",
                 f"Classification id '{entry.classification}' does not exist.",
             )
         choice = finding_classification_choice_model.objects.filter(
             id=entry.choice
         ).first()
         if not choice:
             _api_error(
                 400,
                 "invalid-choice",
                 f"Classification choice id '{entry.choice}' does not exist.",
             )
         assert classification is not None
         assert choice is not None
         _validate_classification_payload(
             finding=patient_finding.finding,
             classification=classification,
             choice=choice,
             module_name=module_name,
         )
         patient_finding_classification_model.objects.create(
             finding=patient_finding,
             classification=classification,
             classification_choice=choice,
             is_active=True,
         )
 
 
 register_report_template_routes(
     api,
     load_module_kb=lambda *args, **kwargs: _load_module_kb(*args, **kwargs),
     clear_kb_caches=lambda: _clear_kb_caches(),
     resolve_payload_kb_identity=lambda *args, **kwargs: _resolve_payload_kb_identity(
         *args, **kwargs
     ),
     orm_models=lambda: _orm_models(),
     build_p_examination_payload_from_host_ledger=lambda *args, **kwargs: (
         _build_p_examination_payload_from_host_ledger(*args, **kwargs)
     ),
     persist_patient_examination_dtypes_record=lambda *args, **kwargs: (
         _persist_patient_examination_dtypes_record(*args, **kwargs)
     ),
     authenticate_request_user=lambda request: _authenticate_request_user(request),
     report_template_access_allowed=lambda actor, capability: (
         _report_template_access_allowed(actor, capability)
     ),
 )
 
 register_findings_routes(
     api,
     load_module_kb=lambda *args, **kwargs: _load_module_kb(*args, **kwargs),
     orm_models=lambda: _orm_models(),
     api_error=lambda *args, **kwargs: _api_error(*args, **kwargs),
     authenticate_request_user=_authenticate_request_user,
     patient_examination_access_allowed=_patient_examination_access_allowed,
     patient_finding_access_allowed=_patient_finding_access_allowed,
     patient_findings_queryset_for_request=_patient_findings_queryset_for_request,
     build_p_examination_payload_from_host_ledger=lambda *args, **kwargs: (
         _build_p_examination_payload_from_host_ledger(*args, **kwargs)
     ),
     persist_patient_examination_dtypes_record=lambda *args, **kwargs: (
         _persist_patient_examination_dtypes_record(*args, **kwargs)
     ),
 )
 
+register_indications_routes(
+    api,
+    orm_models=lambda: _orm_models(),
+    api_error=lambda *args, **kwargs: _api_error(*args, **kwargs),
+)
+
 register_examinations_routes(
     api,
     orm_models=lambda: _orm_models(),
     api_error=lambda *args, **kwargs: _api_error(*args, **kwargs),
 )
 
 register_terminology_routes(
     api,
     clear_kb_caches=lambda: _clear_kb_caches(),
     authenticate_request_user=(
         _authenticate_request_user if _host_integration_is_configured() else None
     ),
     terminology_write_access_allowed=(
         _terminology_write_access_allowed if _host_integration_is_configured() else None
     ),
 )
diff --git lx_dtypes/django/api/report_template_routes.py lx_dtypes/django/api/report_template_routes.py
index 3d928a1..c6ec5d1 100644
--- lx_dtypes/django/api/report_template_routes.py
+++ lx_dtypes/django/api/report_template_routes.py
@@ -1,541 +1,543 @@
 from __future__ import annotations
 
 from typing import Any, Callable, Dict, List, Literal, Mapping, Protocol, TypeVar, cast
 
 from ninja.errors import HttpError  # type: ignore[import-untyped]
 
 from lx_dtypes.models.contracts import KnowledgeBaseContract
 from lx_dtypes.models.interface.ReportTemplateCompiler import ReportTemplateCompiler
 from lx_dtypes.models.interface.ReportTemplateValidator import ReportTemplateValidator
 from lx_dtypes.models.interface.KnowledgeBase import SemanticAdmissibilityError
 from lx_dtypes.models.interface.KnowledgeBaseResolver import load_knowledge_base
 from lx_dtypes.models.interface.KnowledgeBaseResolver import (
     get_knowledge_base_identity,
 )
 from lx_dtypes.models.ledger.p_examination.Pydantic import PExamination
 from lx_dtypes.models.knowledge_base.report_template.ReportConceptCoverageBuilder import (
     build_report_concept_coverage,
 )
 
 from . import report_template_builder
 from .report_template_builder import (
     PublishReportTemplateResponse,
     SaveReportTemplateRequest,
     SaveReportTemplateResponse,
     save_report_template_definition,
     set_saved_report_template_lifecycle,
 )
 from .lookup_tracker import register_runtime_lookup_tracker
 from .request_types import BaseRequest
 
 F = TypeVar("F", bound=Callable[..., Any])
 ReportTemplateCapability = Literal["report_template:read", "report_template:write"]
 
 
 class _RouteDecorator(Protocol):
     def __call__(self, func: F, /) -> F: ...
 
 
 class _TypedApi(Protocol):
     def get(self, path: str, /) -> _RouteDecorator: ...
 
     def post(self, path: str, /) -> _RouteDecorator: ...
 
 
 def _compile_report_template(
     kb: Any,
     template_name: str,
     *,
     mode: Literal["preview", "publish", "production"],
 ) -> Dict[str, Any]:
     validator = ReportTemplateValidator(kb=kb, compiler=ReportTemplateCompiler(kb=kb))
     return validator.validate_and_compile(template_name, mode=mode)
 
 
 def _attach_resolved_kb_identity(
     validation: Mapping[str, Any],
     *,
     module_name: str,
     version: str | None,
 ) -> Dict[str, Any]:
     response = dict(validation)
     response["knowledge_base_module"] = module_name
     response["knowledge_base_version"] = version
     return response
 
 
 def _load_builder_module_kb(module_name: str) -> KnowledgeBaseContract:
     _, resolved_version = get_knowledge_base_identity(
         module_name,
         input_dirs=[report_template_builder.MODULES_ROOT],
     )
     kb = cast(
         KnowledgeBaseContract,
         load_knowledge_base(
             module_name,
             version=resolved_version,
             input_dirs=[report_template_builder.MODULES_ROOT],
         ),
     )
     register_runtime_lookup_tracker(cast(Any, kb))
     return kb
 
 
 def register_report_template_routes(
     api: _TypedApi,
     *,
     load_module_kb: Callable[..., KnowledgeBaseContract],
     clear_kb_caches: Callable[[], None],
     resolve_payload_kb_identity: Callable[[str, PExamination], tuple[str, str | None]],
     orm_models: Callable[[], Dict[str, Any]],
     build_p_examination_payload_from_host_ledger: Callable[..., PExamination],
     persist_patient_examination_dtypes_record: Callable[
         [object, PExamination], dict[str, Any]
     ]
     | None = None,
     authenticate_request_user: Callable[[BaseRequest], Any | None],
     report_template_access_allowed: Callable[[object, ReportTemplateCapability], bool],
 ) -> None:
     def require_builder_access(
         request: BaseRequest, capability: ReportTemplateCapability
     ) -> None:
         actor = authenticate_request_user(request)
         if actor is None:
             raise HttpError(401, "Authentication is required.")
         if not report_template_access_allowed(actor, capability):
             raise HttpError(
                 403,
                 f"{capability} access is required for report-template builder routes.",
             )
 
     @api.post("/report-templates/builder/templates")
     def save_report_template(
         request: BaseRequest,
         payload: SaveReportTemplateRequest,
     ) -> SaveReportTemplateResponse:
         """
         Persist a new report-template YAML file into one lx_dtypes knowledge-base module.
         """
         require_builder_access(request, "report_template:write")
         try:
             saved = save_report_template_definition(payload)
         except FileExistsError as exc:
             raise HttpError(409, str(exc)) from exc
         except ValueError as exc:
             raise HttpError(400, str(exc)) from exc
 
         clear_kb_caches()
         kb = _load_builder_module_kb(saved.module_name)
         compiled = _compile_report_template(kb, saved.template_name, mode="preview")
         saved.readiness = compiled["summary"].model_dump(mode="json")
         return saved
 
     @api.get("/report-templates/by-examination/{module_name}/{examination_name}")
     def report_templates_by_examination(
         request: BaseRequest, module_name: str, examination_name: str
     ) -> List[Dict[str, Any]]:
         """
         Return all resolved report templates for the given examination in one module.
         """
         del request
         kb = load_module_kb(module_name)
         matches: list[Dict[str, Any]] = []
-        for template_name, template in kb.report_template.items():
+        for template_name, template in cast(Mapping[str, Any], kb.report_template).items():
+            template = cast(Any, template)
             if template.examination != examination_name:
                 continue
             if kb.get_report_template_lifecycle_status(template_name) != "published":
                 continue
             compiled = _compile_report_template(kb, template_name, mode="production")
             if not compiled["summary"].can_publish:
                 continue
             matches.append(kb.export_report_template(template_name))
         return matches
 
     @api.get(
         "/report-templates/builder/by-examination/{module_name}/{examination_name}"
     )
     def builder_report_templates_by_examination(
         request: BaseRequest, module_name: str, examination_name: str
     ) -> List[Dict[str, Any]]:
         """Return preview exports for all builder templates, including drafts."""
         require_builder_access(request, "report_template:read")
         kb = load_module_kb(module_name)
         matches: list[Dict[str, Any]] = []
-        for template_name, template in kb.report_template.items():
+        for template_name, template in cast(Mapping[str, Any], kb.report_template).items():
+            template = cast(Any, template)
             if template.examination != examination_name:
                 continue
             matches.append(kb.export_report_template_preview(template_name))
         return matches
 
     @api.get("/report-templates/{module_name}/{template_name}")
     def report_template_by_name(
         request: BaseRequest, module_name: str, template_name: str
     ) -> Dict[str, Any]:
         """
         Return a resolved report template JSON payload by module/template name.
         """
         del request
         kb = load_module_kb(module_name)
         try:
             return kb.export_report_template(template_name)
         except KeyError as exc:
             raise HttpError(
                 404,
                 f"Published report template '{template_name}' not found in module '{module_name}'.",
             ) from exc
 
     @api.get("/report-templates/{module_name}/{template_name}/preview")
     def preview_report_template_by_name(
         request: BaseRequest, module_name: str, template_name: str
     ) -> Dict[str, Any]:
         require_builder_access(request, "report_template:read")
         kb = load_module_kb(module_name)
         try:
             return kb.export_report_template_preview(template_name)
         except KeyError as exc:
             raise HttpError(
                 404,
                 f"Report template '{template_name}' not found in module '{module_name}'.",
             ) from exc
 
     @api.post(
         "/report-templates/builder/templates/{module_name}/{template_name}/publish"
     )
     def publish_report_template(
         request: BaseRequest, module_name: str, template_name: str
     ) -> PublishReportTemplateResponse:
         require_builder_access(request, "report_template:write")
         _, resolved_version = get_knowledge_base_identity(
             module_name,
             input_dirs=[report_template_builder.MODULES_ROOT],
         )
         kb = load_module_kb(module_name, version=resolved_version)
         try:
             compiled = _compile_report_template(kb, template_name, mode="publish")
         except KeyError as exc:
             raise HttpError(
                 404,
                 f"Report template '{template_name}' not found in module '{module_name}'.",
             ) from exc
 
         summary = compiled["summary"]
         if not summary.can_publish:
             raise HttpError(
                 409,
                 f"Report template '{template_name}' cannot be published until blocking issues are resolved.",
             )
 
         response = set_saved_report_template_lifecycle(
             module_name=module_name,
             template_name=template_name,
             lifecycle_status="published",
         )
         clear_kb_caches()
         refreshed_kb = load_module_kb(module_name, version=resolved_version)
         refreshed = _compile_report_template(
             refreshed_kb, template_name, mode="production"
         )
         response.readiness = refreshed["summary"].model_dump(mode="json")
         return response
 
     @api.post(
         "/report-templates/builder/templates/{module_name}/{template_name}/unpublish"
     )
     def unpublish_report_template(
         request: BaseRequest, module_name: str, template_name: str
     ) -> PublishReportTemplateResponse:
         require_builder_access(request, "report_template:write")
         _, resolved_version = get_knowledge_base_identity(
             module_name,
             input_dirs=[report_template_builder.MODULES_ROOT],
         )
         kb = load_module_kb(module_name, version=resolved_version)
         if template_name not in kb.report_template:
             raise HttpError(
                 404,
                 f"Report template '{template_name}' not found in module '{module_name}'.",
             )
 
         response = set_saved_report_template_lifecycle(
             module_name=module_name,
             template_name=template_name,
             lifecycle_status="draft",
         )
         clear_kb_caches()
         refreshed_kb = load_module_kb(module_name, version=resolved_version)
         refreshed = _compile_report_template(
             refreshed_kb, template_name, mode="preview"
         )
         response.readiness = refreshed["summary"].model_dump(mode="json")
         return response
 
     @api.post("/report-templates/{module_name}/{template_name}/validate")
     def validate_report_template_runtime(
         request: BaseRequest,
         module_name: str,
         template_name: str,
         payload: PExamination,
     ) -> Dict[str, Any]:
         """
         Execute report-template validator logic against typed patient examination state.
         """
         del request
         resolved_module_name, resolved_version = resolve_payload_kb_identity(
             module_name, payload
         )
         kb = load_module_kb(resolved_module_name, version=resolved_version)
         try:
             template_export = kb.export_report_template(template_name)
             validation = kb.evaluate_report_template_validators(
                 template_name, p_examination=payload
             )
             response = _attach_resolved_kb_identity(
                 validation,
                 module_name=resolved_module_name,
                 version=resolved_version,
             )
             response["concept_coverage"] = build_report_concept_coverage(
                 kb=cast(Any, kb),
                 requested_template_name=template_name,
                 template_export=template_export,
                 p_examination=payload,
                 validation=validation,
             ).model_dump(mode="json")
             return response
         except SemanticAdmissibilityError as exc:
             raise HttpError(422, str(exc)) from exc
         except ValueError as exc:
             raise HttpError(422, str(exc)) from exc
         except KeyError as exc:
             raise HttpError(
                 404,
                 f"Published report template '{template_name}' not found in module '{module_name}'.",
             ) from exc
 
     @api.get("/patient-examinations/{patient_examination_id}/dtypes-record/")
     def get_patient_examination_dtypes_record(
         request: BaseRequest,
         patient_examination_id: int,
     ) -> Dict[str, Any]:
         del request
         patient_examination_model = orm_models()["PatientExamination"]
         patient_examination = patient_examination_model.objects.filter(
             id=patient_examination_id
         ).first()
         if not patient_examination:
             raise HttpError(
                 404,
                 f"PatientExamination '{patient_examination_id}' not found.",
             )
         record = getattr(patient_examination, "dtypes_record", None)
         if not isinstance(record, dict):
             return {}
         return cast(Dict[str, Any], record)
 
     @api.post("/patient-examinations/{patient_examination_id}/dtypes-record/")
     def persist_patient_examination_dtypes_record_route(
         request: BaseRequest,
         patient_examination_id: int,
         payload: PExamination,
     ) -> Dict[str, Any]:
         del request
         patient_examination_model = orm_models()["PatientExamination"]
         patient_examination = patient_examination_model.objects.filter(
             id=patient_examination_id
         ).first()
         if not patient_examination:
             raise HttpError(
                 404,
                 f"PatientExamination '{patient_examination_id}' not found.",
             )
         if persist_patient_examination_dtypes_record is None:
             raise HttpError(501, "dtypes record persistence is not configured.")
         try:
             return persist_patient_examination_dtypes_record(
                 patient_examination,
                 payload,
             )
         except ValueError as exc:
             raise HttpError(422, str(exc)) from exc
 
     @api.post(
         "/report-templates/{module_name}/{template_name}/validate-from-ledger/{patient_examination_id}"
     )
     def validate_report_template_runtime_from_ledger(
         request: BaseRequest,
         module_name: str,
         template_name: str,
         patient_examination_id: int,
     ) -> Dict[str, Any]:
         del request
         patient_examination_model = orm_models()["PatientExamination"]
         patient_examination = patient_examination_model.objects.filter(
             id=patient_examination_id
         ).first()
         if not patient_examination:
             raise HttpError(
                 404,
                 f"PatientExamination '{patient_examination_id}' not found.",
             )
 
         try:
             payload = build_p_examination_payload_from_host_ledger(
                 patient_examination, route_module_name=module_name
             )
         except ValueError as exc:
             raise HttpError(422, str(exc)) from exc
 
         resolved_module_name, resolved_version = resolve_payload_kb_identity(
             module_name, payload
         )
         kb = load_module_kb(resolved_module_name, version=resolved_version)
         try:
             template_export = kb.export_report_template(template_name)
             validation = kb.evaluate_report_template_validators(
                 template_name, p_examination=payload
             )
             response = _attach_resolved_kb_identity(
                 validation,
                 module_name=resolved_module_name,
                 version=resolved_version,
             )
             response["concept_coverage"] = build_report_concept_coverage(
                 kb=cast(Any, kb),
                 requested_template_name=template_name,
                 template_export=template_export,
                 p_examination=payload,
                 validation=validation,
             ).model_dump(mode="json")
             return response
         except SemanticAdmissibilityError as exc:
             raise HttpError(422, str(exc)) from exc
         except ValueError as exc:
             raise HttpError(422, str(exc)) from exc
         except KeyError as exc:
             raise HttpError(
                 404,
                 f"Published report template '{template_name}' not found in module '{module_name}'.",
             ) from exc
 
     @api.get("/report-templates/{module_name}/{template_name}/validate-definition")
     def validate_report_template_definition(
         request: BaseRequest, module_name: str, template_name: str
     ) -> Dict[str, Any]:
         require_builder_access(request, "report_template:read")
         _, resolved_version = get_knowledge_base_identity(
             module_name,
             input_dirs=[report_template_builder.MODULES_ROOT],
         )
         kb = load_module_kb(module_name, version=resolved_version)
         if template_name not in kb.report_template:
             raise HttpError(
                 404,
                 f"Report template '{template_name}' not found in module '{module_name}'.",
             )
         try:
             compiled = _compile_report_template(kb, template_name, mode="preview")
         except KeyError as exc:
             raise HttpError(
                 404,
                 f"Report template '{template_name}' not found in module '{module_name}'.",
             ) from exc
         return cast(Dict[str, Any], compiled["summary"].model_dump(mode="json"))
 
     @api.post("/validators/{module_name}/{validator_kind}/{validator_name}/validate")
     def validate_single_validator_runtime(
         request: BaseRequest,
         module_name: str,
         validator_kind: str,
         validator_name: str,
         payload: PExamination,
     ) -> Dict[str, Any]:
         del request
         resolved_module_name, resolved_version = resolve_payload_kb_identity(
             module_name, payload
         )
         kb = load_module_kb(resolved_module_name, version=resolved_version)
 
         if validator_kind == "findings_validator":
             if validator_name not in kb.findings_validator:
                 raise HttpError(404, f"Unknown findings validator '{validator_name}'.")
             try:
                 validation = kb.evaluate_findings_validator(
                     validator_name, p_examination=payload
                 )
                 return _attach_resolved_kb_identity(
                     validation,
                     module_name=resolved_module_name,
                     version=resolved_version,
                 )
             except SemanticAdmissibilityError as exc:
                 raise HttpError(422, str(exc)) from exc
 
         if validator_kind == "classification_validator":
             if validator_name not in kb.classification_validator:
                 raise HttpError(
                     404, f"Unknown classification validator '{validator_name}'."
                 )
             try:
                 validation = kb.evaluate_classification_validator(
                     validator_name,
                     p_examination=payload,
                 )
                 return _attach_resolved_kb_identity(
                     validation,
                     module_name=resolved_module_name,
                     version=resolved_version,
                 )
             except SemanticAdmissibilityError as exc:
                 raise HttpError(422, str(exc)) from exc
 
         if validator_kind == "intervention_validator":
             if validator_name not in kb.intervention_validator:
                 raise HttpError(
                     404, f"Unknown intervention validator '{validator_name}'."
                 )
             try:
                 validation = kb.evaluate_intervention_validator(
                     validator_name, p_examination=payload
                 )
                 return _attach_resolved_kb_identity(
                     validation,
                     module_name=resolved_module_name,
                     version=resolved_version,
                 )
             except SemanticAdmissibilityError as exc:
                 raise HttpError(422, str(exc)) from exc
 
         if validator_kind == "unit_validator":
             if validator_name not in kb.unit_validator:
                 raise HttpError(404, f"Unknown unit validator '{validator_name}'.")
             try:
                 validation = kb.evaluate_unit_validator(
                     validator_name, p_examination=payload
                 )
                 return _attach_resolved_kb_identity(
                     validation,
                     module_name=resolved_module_name,
                     version=resolved_version,
                 )
             except SemanticAdmissibilityError as exc:
                 raise HttpError(422, str(exc)) from exc
 
         if validator_kind == "examination_validator":
             if validator_name not in kb.examination_validator:
                 raise HttpError(
                     404, f"Unknown examination validator '{validator_name}'."
                 )
             try:
                 validation = kb.evaluate_examination_validator(
                     validator_name,
                     p_examination=payload,
                 )
                 return _attach_resolved_kb_identity(
                     validation,
                     module_name=resolved_module_name,
                     version=resolved_version,
                 )
             except SemanticAdmissibilityError as exc:
                 raise HttpError(422, str(exc)) from exc
 
         raise HttpError(404, f"Unknown validator kind '{validator_kind}'.")
diff --git lx_dtypes/django/api/tests/test_findings_api.py lx_dtypes/django/api/tests/test_findings_api.py
index 3669200..55ec9ba 100644
--- lx_dtypes/django/api/tests/test_findings_api.py
+++ lx_dtypes/django/api/tests/test_findings_api.py
@@ -1,851 +1,948 @@
 import json
 from datetime import date
 from importlib import import_module
 from typing import Any
 
 import pytest
 from django.contrib.auth import get_user_model
 from django.conf import settings
 from django.test import Client
 
 try:
     host_models_module = getattr(settings, "LX_DTYPES_HOST_MODELS_MODULE", None)
     if not host_models_module:
         raise ModuleNotFoundError
     host_models = import_module(host_models_module)
     Center = host_models.Center
     Examination = host_models.Examination
     Finding = host_models.Finding
     FindingClassification = host_models.FindingClassification
     FindingClassificationChoice = host_models.FindingClassificationChoice
     Gender = host_models.Gender
+    Indication = host_models.Indication
     Patient = host_models.Patient
     PatientExamination = host_models.PatientExamination
 except (ModuleNotFoundError, RuntimeError, AttributeError):  # pragma: no cover
     pytest.skip(
         "Host application models are required for lx_dtypes Django API integration tests.",
         allow_module_level=True,
     )
 
 from lx_dtypes.django.api import findings_routes, terminology_routes
 
 pytestmark = pytest.mark.django_db
 
 
 def _create_patient_examination(examination: Any) -> Any:
     gender, _ = Gender.objects.get_or_create(name="male")
     center, _ = Center.objects.get_or_create(name="Test Center")
     patient = Patient.objects.create(
         first_name="Base",
         last_name="Api",
         dob=date(1980, 1, 1),
         gender=gender,
         center=center,
     )
     return PatientExamination.objects.create(
         patient=patient,
         examination=examination,
         hash=f"pe-{patient.id}-{examination.id}",
     )
 
 
 def _create_exam_graph() -> tuple[Any, Any, Any, Any]:
     examination = Examination.objects.create(name="colonoscopy")
     finding = Finding.objects.create(name="colon_polyp")
     examination.findings.add(finding)
     classification = FindingClassification.objects.create(
         name="size_classification",
         description="Size category",
     )
     choice_small = FindingClassificationChoice.objects.create(
         name="small_polyp",
         description="small",
         subcategories={},
         numerical_descriptors={},
     )
     classification.choices.add(choice_small)
     finding.finding_classifications.add(classification)
     return examination, finding, classification, choice_small
 
 
 def _mock_kb_lookup(module_name: str, version: str | None = None) -> dict[str, Any]:
     if module_name != "catalog_module":
         return {
             "examination": {},
             "finding": {},
             "classification": {},
             "classification_choice": {},
+            "indication": {},
         }
     normalized_version = str(version or "").strip()
     if normalized_version == "1.0.0":
         return {
             "examination": {
                 "colonoscopy": {
                     "name": "colonoscopy",
                     "findings": ["colon_polyp"],
+                    "indications": ["screening"],
                 }
             },
             "finding": {
                 "colon_polyp": {
                     "name": "colon_polyp",
                     "classifications": ["size_classification"],
                 }
             },
             "classification": {
                 "size_classification": {
                     "name": "size_classification",
                     "classification_choices": ["small_polyp"],
                 }
             },
             "classification_choice": {
                 "small_polyp": {"name": "small_polyp"},
             },
+            "indication": {
+                "screening": {"name": "screening"},
+            },
         }
     if normalized_version == "2.0.0":
         return {
             "examination": {
                 "colonoscopy": {
                     "name": "colonoscopy",
                     "findings": [],
+                    "indications": [],
                 }
             },
             "finding": {},
             "classification": {},
             "classification_choice": {},
+            "indication": {},
         }
     return {
         "examination": {},
         "finding": {},
         "classification": {},
         "classification_choice": {},
+        "indication": {},
     }
 
 
+def _create_exam_graph_with_indication() -> tuple[Any, Any, Any, Any, Any]:
+    examination = Examination.objects.create(name="colonoscopy")
+    finding = Finding.objects.create(name="colon_polyp")
+    examination.findings.add(finding)
+    classification = FindingClassification.objects.create(
+        name="size_classification",
+        description="Size category",
+    )
+    choice_small = FindingClassificationChoice.objects.create(
+        name="small_polyp",
+        description="small",
+        subcategories={},
+        numerical_descriptors={},
+    )
+    classification.choices.add(choice_small)
+    finding.finding_classifications.add(classification)
+    indication = Indication.objects.create(name="screening")
+    examination.indications.add(indication)
+    return examination, finding, classification, choice_small, indication
+
+
 def test_base_api_findings_read_endpoints_shape() -> None:
     client = Client()
-    examination, finding, classification, choice = _create_exam_graph()
+    examination, finding, classification, choice, indication = (
+        _create_exam_graph_with_indication()
+    )
 
     findings_res = client.get(
         f"/base_api/examinations/{examination.id}/findings/",
         secure=True,
     )
     assert findings_res.status_code == 200, findings_res.content.decode()
     findings_payload = findings_res.json()
     assert isinstance(findings_payload, list)
     assert findings_payload
     first_finding = findings_payload[0]
     assert first_finding["id"] == finding.id
     assert "classifications" in first_finding
 
     classifications_res = client.get(
         f"/base_api/findings/{finding.id}/classifications/",
         secure=True,
     )
     assert classifications_res.status_code == 200
     classifications_payload = classifications_res.json()
     assert isinstance(classifications_payload, list)
     assert classifications_payload[0]["id"] == classification.id
     assert isinstance(classifications_payload[0].get("choices"), list)
 
     choices_res = client.get(
         f"/base_api/classifications/{classification.id}/choices/",
         secure=True,
     )
     assert choices_res.status_code == 200
     choices_payload = choices_res.json()
     assert "choices" in choices_payload
     assert choices_payload["choices"][0]["id"] == choice.id
 
+    indications_res = client.get(
+        f"/base_api/examinations/{examination.id}/indications/",
+        secure=True,
+    )
+    assert indications_res.status_code == 200
+    indications_payload = indications_res.json()
+    assert isinstance(indications_payload, list)
+    assert indications_payload[0]["name"] == "screening"
+
+    indications_tree_res = client.get(
+        "/base_api/indications/tree/",
+        secure=True,
+    )
+    assert indications_tree_res.status_code == 200
+    indications_tree_payload = indications_tree_res.json()
+    assert isinstance(indications_tree_payload, list)
+    tree_screening = next(
+        (item for item in indications_tree_payload if item["name"] == "screening"), None
+    )
+    assert tree_screening is not None
+    assert any(
+        examination_entry["id"] == examination.id
+        for examination_entry in tree_screening.get("examinations", [])
+    )
+
 
 def test_base_api_findings_read_endpoints_support_module_version_overrides(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     client = Client()
-    examination, finding, classification, choice = _create_exam_graph()
+    examination, finding, classification, choice, indication = (
+        _create_exam_graph_with_indication()
+    )
     monkeypatch.setattr(findings_routes, "_kb_lookup", _mock_kb_lookup)
 
     legacy_findings = client.get(
         f"/base_api/examinations/{examination.id}/findings/?module_name=catalog_module&module_version=1.0.0",
         secure=True,
     )
     assert legacy_findings.status_code == 200
     assert len(legacy_findings.json()) == 1
     assert legacy_findings.json()[0]["id"] == finding.id
 
     modern_findings = client.get(
         f"/base_api/examinations/{examination.id}/findings/?module_name=catalog_module&module_version=2.0.0",
         secure=True,
     )
     assert modern_findings.status_code == 200
     assert modern_findings.json() == []
 
     legacy_classifications = client.get(
         f"/base_api/findings/{finding.id}/classifications/?module_name=catalog_module&module_version=1.0.0",
         secure=True,
     )
     assert legacy_classifications.status_code == 200
     assert any(
         item["name"] == classification.name for item in legacy_classifications.json()
     )
 
     modern_classifications = client.get(
         f"/base_api/findings/{finding.id}/classifications/?module_name=catalog_module&module_version=2.0.0",
         secure=True,
     )
     assert modern_classifications.status_code == 200
     assert modern_classifications.json() == []
 
     legacy_choices = client.get(
         f"/base_api/classifications/{classification.id}/choices/?module_name=catalog_module&module_version=1.0.0",
         secure=True,
     )
     assert legacy_choices.status_code == 200
     assert legacy_choices.json()["choices"][0]["id"] == choice.id
 
     modern_choices = client.get(
         f"/base_api/classifications/{classification.id}/choices/?module_name=catalog_module&module_version=2.0.0",
         secure=True,
     )
     assert modern_choices.status_code == 200
     assert modern_choices.json() == {"choices": []}
 
+    legacy_indications = client.get(
+        f"/base_api/examinations/{examination.id}/indications/?module_name=catalog_module&module_version=1.0.0",
+        secure=True,
+    )
+    assert legacy_indications.status_code == 200
+    assert len(legacy_indications.json()) == 1
+    assert legacy_indications.json()[0]["id"] == indication.id
+
+    modern_indications = client.get(
+        f"/base_api/examinations/{examination.id}/indications/?module_name=catalog_module&module_version=2.0.0",
+        secure=True,
+    )
+    assert modern_indications.status_code == 200
+    assert modern_indications.json() == []
+
+    legacy_tree = client.get(
+        "/base_api/indications/tree/?module_name=catalog_module&module_version=1.0.0",
+        secure=True,
+    )
+    assert legacy_tree.status_code == 200
+    legacy_tree_payload = legacy_tree.json()
+    assert isinstance(legacy_tree_payload, list)
+    assert any(node["name"] == indication.name for node in legacy_tree_payload)
+    legacy_screening_node = next(
+        node for node in legacy_tree_payload if node["name"] == indication.name
+    )
+    assert any(
+        examination_entry["id"] == examination.id
+        for examination_entry in legacy_screening_node.get("examinations", [])
+    )
+
+    modern_tree = client.get(
+        "/base_api/indications/tree/?module_name=catalog_module&module_version=2.0.0",
+        secure=True,
+    )
+    assert modern_tree.status_code == 200
+    assert modern_tree.json() == []
+
 
 def test_base_api_findings_read_routes_use_patient_examination_kb_when_requested(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     client = Client()
     examination, _, _, _ = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
     patient_examination.knowledge_base_module = "catalog_module"
     patient_examination.knowledge_base_version = "1.0.0"
     patient_examination.save(
         update_fields=["knowledge_base_module", "knowledge_base_version"]
     )
 
     monkeypatch.setattr(
         terminology_routes,
         "active_terminology_selection",
         lambda: ("catalog_module", "2.0.0"),
     )
     monkeypatch.setattr(findings_routes, "_kb_lookup", _mock_kb_lookup)
 
     response = client.get(
         f"/base_api/examinations/{examination.id}/findings/?patient_examination_id={patient_examination.id}",
         secure=True,
     )
     assert response.status_code == 200
     assert len(response.json()) == 1
 
 
 def test_base_api_findings_read_routes_fallback_to_active_for_unpinned_patient_examination(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     client = Client()
     examination, _, _, _ = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
     patient_examination.knowledge_base_module = ""
     patient_examination.knowledge_base_version = ""
     patient_examination.save(
         update_fields=["knowledge_base_module", "knowledge_base_version"]
     )
 
     monkeypatch.setattr(
         terminology_routes,
         "active_terminology_selection",
         lambda: ("catalog_module", "2.0.0"),
     )
     monkeypatch.setattr(findings_routes, "_kb_lookup", _mock_kb_lookup)
 
     response = client.get(
         f"/base_api/examinations/{examination.id}/findings/?patient_examination_id={patient_examination.id}",
         secure=True,
     )
     assert response.status_code == 200
     assert response.json() == []
 
 
 def test_base_api_patient_examination_findings_read_uses_pinned_kb_when_examination_context_present(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     client = Client()
     examination = Examination.objects.create(name="colonoscopy")
     finding_v1 = Finding.objects.create(name="POLYP-V1")
     finding_v2 = Finding.objects.create(name="POLYP-V2")
     examination.findings.add(finding_v1, finding_v2)
 
     patient_examination = _create_patient_examination(examination)
     patient_examination.knowledge_base_module = "catalog_module"
     patient_examination.knowledge_base_version = "1.0.0"
     patient_examination.save(
         update_fields=["knowledge_base_module", "knowledge_base_version"]
     )
 
     def _mock_catalog_kb_lookup(
         module_name: str, version: str | None = None
     ) -> dict[str, Any]:
         if module_name != "catalog_module":
             return {
                 "examination": {},
                 "finding": {},
                 "classification": {},
                 "classification_choice": {},
             }
         normalized_version = str(version or "").strip()
         if normalized_version == "1.0.0":
             return {
                 "examination": {
                     "colonoscopy": {
                         "name": "colonoscopy",
                         "findings": ["POLYP-V1"],
                     }
                 },
                 "finding": {
                     "polyp-v1": {"name": "POLYP-V1", "classifications": []},
                 },
                 "classification": {},
                 "classification_choice": {},
             }
         if normalized_version == "2.0.0":
             return {
                 "examination": {
                     "colonoscopy": {
                         "name": "colonoscopy",
                         "findings": ["POLYP-V2"],
                     }
                 },
                 "finding": {
                     "polyp-v2": {"name": "POLYP-V2", "classifications": []},
                 },
                 "classification": {},
                 "classification_choice": {},
             }
         return {
             "examination": {},
             "finding": {},
             "classification": {},
             "classification_choice": {},
         }
 
     monkeypatch.setattr(
         terminology_routes,
         "active_terminology_selection",
         lambda: ("catalog_module", "2.0.0"),
     )
     monkeypatch.setattr(findings_routes, "_kb_lookup", _mock_catalog_kb_lookup)
 
     response = client.get(
         f"/base_api/examinations/{examination.id}/findings/",
         secure=True,
     )
 
     assert response.status_code == 200, response.content.decode()
     payload = response.json()
     assert any(item["id"] == finding_v1.id for item in payload)
     assert not any(item["id"] == finding_v2.id for item in payload)
 
 
 def test_base_api_patient_findings_uses_examination_pinned_kb_for_create_and_rejects_other_version(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     client = Client()
     user_model = get_user_model()
     user = user_model.objects.create(
         username="legacy-kb-create",
         is_staff=True,
     )
     client.force_login(user)
 
     examination = Examination.objects.create(name="colonoscopy")
     finding_v1 = Finding.objects.create(name="POLYP-V1")
     finding_v2 = Finding.objects.create(name="POLYP-V2")
     examination.findings.add(finding_v1, finding_v2)
     patient_examination = _create_patient_examination(examination)
     patient_examination.knowledge_base_module = "catalog_module"
     patient_examination.knowledge_base_version = "1.0.0"
     patient_examination.save(
         update_fields=["knowledge_base_module", "knowledge_base_version"]
     )
 
     monkeypatch.setattr(
         findings_routes,
         "_kb_lookup",
         lambda _module_name, version=None: {
             "examination": {
                 "colonoscopy": {
                     "name": "colonoscopy",
                     "findings": [
                         "POLYP-V1" if str(version or "") == "1.0.0" else "POLYP-V2"
                     ],
                 }
             },
             "finding": {
                 "polyp-v1": {"name": "POLYP-V1", "classifications": []},
                 "polyp-v2": {"name": "POLYP-V2", "classifications": []},
             },
             "classification": {},
             "classification_choice": {},
         },
     )
 
     # active catalog is v2.0.0, but the exam is pinned to v1.0.0.
     monkeypatch.setattr(
         terminology_routes,
         "active_terminology_selection",
         lambda: ("catalog_module", "2.0.0"),
     )
 
     allowed_response = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding_v1.id,
                 "classifications": [],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert allowed_response.status_code == 200, allowed_response.content.decode()
 
     blocked_response = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding_v2.id,
                 "classifications": [],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert blocked_response.status_code == 400, blocked_response.content.decode()
     assert blocked_response.json().get("code") == "invalid-finding"
 
 
 def test_base_api_patient_findings_create_fails_closed_when_access_check_callback_is_missing(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     client = Client()
     user_model = get_user_model()
     user = user_model.objects.create(
         username="missing-access-callback",
         is_staff=True,
     )
     client.force_login(user)
     examination, finding, _, _ = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
 
     monkeypatch.delattr(
         host_models, "patient_examination_access_allowed", raising=False
     )
 
     response = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding.id,
                 "classifications": [],
             }
         ),
         content_type="application/json",
         secure=True,
     )
 
     assert response.status_code == 404, response.content.decode()
     assert response.json().get("code") == "not-found"
 
 
 def test_base_api_patient_findings_crud_and_classifications() -> None:
     client = Client()
     user_model = get_user_model()
     user = user_model.objects.create(
         username="dtypes-findings-admin",
         is_staff=True,
     )
     client.force_login(user)
     examination, finding, classification, choice = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
 
     create_res = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding.id,
                 "classifications": [
                     {"classification": classification.id, "choice": choice.id}
                 ],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert create_res.status_code == 200, create_res.content.decode()
     created_payload = create_res.json()
     assert created_payload["patient_examination"] == patient_examination.id
     assert created_payload["finding"] == finding.id
     patient_finding_id = created_payload["id"]
 
     list_res = client.get(
         f"/base_api/patient-findings/?patient_examination={patient_examination.id}",
         secure=True,
     )
     assert list_res.status_code == 200
     listed = list_res.json()
     assert isinstance(listed, list)
     assert len(listed) == 1
     assert listed[0]["id"] == patient_finding_id
     assert len(listed[0]["classifications"]) == 1
 
     set_classifications_res = client.post(
         f"/base_api/patient-findings/{patient_finding_id}/classifications/",
         data=json.dumps(
             {
                 "replace": True,
                 "classifications": [
                     {"classification": classification.id, "choice": choice.id}
                 ],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert set_classifications_res.status_code == 200
     assert len(set_classifications_res.json()["classifications"]) == 1
 
     patch_res = client.patch(
         f"/base_api/patient-findings/{patient_finding_id}/",
         data=json.dumps({"is_active": True}),
         content_type="application/json",
         secure=True,
     )
     assert patch_res.status_code == 200
     assert patch_res.json()["id"] == patient_finding_id
 
     delete_res = client.delete(
         f"/base_api/patient-findings/{patient_finding_id}/",
         secure=True,
     )
     assert delete_res.status_code == 200
     assert delete_res.json()["success"] is True
 
     list_after_delete = client.get(
         f"/base_api/patient-findings/?patient_examination={patient_examination.id}",
         secure=True,
     )
     assert list_after_delete.status_code == 200
     assert list_after_delete.json() == []
 
 
 def test_base_api_report_template_endpoints_shape() -> None:
     client = Client()
 
     by_name_res = client.get(
         "/base_api/report-templates/report_template_examples/colonoscopy_training_basic",
         secure=True,
     )
     assert by_name_res.status_code == 200, by_name_res.content.decode()
     by_name_payload = by_name_res.json()
     assert by_name_payload["name"] == "colonoscopy_training_basic"
     assert by_name_payload["examination"] == "colonoscopy"
     assert len(by_name_payload["report_sections"]) == 6
     assert by_name_payload["coverage_version"] == "report_concept_coverage_v1"
 
     by_exam_res = client.get(
         "/base_api/report-templates/by-examination/report_template_examples/colonoscopy",
         secure=True,
     )
     assert by_exam_res.status_code == 200, by_exam_res.content.decode()
     by_exam_payload = by_exam_res.json()
     assert isinstance(by_exam_payload, list)
     assert by_exam_payload
     assert by_exam_payload[0]["name"] == "colonoscopy_training_basic"
 
     core_concepts_res = client.get(
         "/base_api/core-concepts/report_template_examples",
         secure=True,
     )
     assert core_concepts_res.status_code == 200, core_concepts_res.content.decode()
     core_concepts_payload = core_concepts_res.json()
     assert "examination" in core_concepts_payload
     assert "finding" in core_concepts_payload
     assert core_concepts_payload["knowledge_base_module"] == "report_template_examples"
     assert core_concepts_payload["knowledge_base_version"] is not None
 
 
 def test_base_api_report_template_runtime_validation() -> None:
     client = Client()
 
     missing_findings_res = client.post(
         "/base_api/report-templates/report_template_examples/colonoscopy_training_basic/validate",
         data=json.dumps(
             {
                 "patient": "test_patient",
                 "examination": "colonoscopy",
                 "patient_findings": [],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert missing_findings_res.status_code == 200, (
         missing_findings_res.content.decode()
     )
     missing_findings_payload = missing_findings_res.json()
     assert missing_findings_payload["template_name"] == "colonoscopy_training_basic"
     assert missing_findings_payload["ok"] is False
     assert any(
         issue["code"] == "finding_not_present"
         and issue["validator_name"] == "koloskopie_sedierung_dokumentiert"
         for issue in missing_findings_payload["issues"]
     )
 
     partial_findings_res = client.post(
         "/base_api/report-templates/report_template_examples/colonoscopy_training_basic/validate",
         data=json.dumps(
             {
                 "patient": "test_patient",
                 "examination": "colonoscopy",
                 "patient_findings": [
                     {
                         "finding": "colonoscopy_deepest_viewed_location",
                         "patient_examination": "test_exam",
                         "patient_finding_classifications": [],
                         "patient_finding_interventions": [],
                     }
                 ],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert partial_findings_res.status_code == 200, (
         partial_findings_res.content.decode()
     )
     partial_findings_payload = partial_findings_res.json()
     assert partial_findings_payload["ok"] is False
     assert partial_findings_payload["evaluated_findings_count"] == 1
     assert partial_findings_payload["examination_validators"][0]["ok"] is False
     assert partial_findings_payload["findings_validators"][0]["ok"] is False
 
 
 def test_base_api_report_template_runtime_validation_from_ledger() -> None:
     client = Client()
     examination = Examination.objects.create(name="colonoscopy")
     patient_examination = _create_patient_examination(examination)
 
     response = client.post(
         (
             "/base_api/report-templates/report_template_examples/"
             f"colonoscopy_training_basic/validate-from-ledger/{patient_examination.id}"
         ),
         secure=True,
     )
 
     assert response.status_code == 200, response.content.decode()
     payload = response.json()
     assert payload["template_name"] == "colonoscopy_training_basic"
     assert payload["evaluated_findings_count"] == 0
     assert payload["ok"] is False
     assert any(issue["code"] == "finding_not_present" for issue in payload["issues"])
 
 
 def test_base_api_report_template_runtime_validation_from_ledger_not_found() -> None:
     client = Client()
     response = client.post(
         (
             "/base_api/report-templates/report_template_examples/"
             "colonoscopy_training_basic/validate-from-ledger/999999"
         ),
         secure=True,
     )
     assert response.status_code == 404
     assert "PatientExamination '999999' not found." in response.content.decode()
 
 
 def test_base_api_patient_findings_validation_invalid_choice() -> None:
     client = Client()
     examination, finding, classification, _choice = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
 
     invalid_choice = FindingClassificationChoice.objects.create(
         name="invalid_for_classification",
         description="invalid",
         subcategories={},
         numerical_descriptors={},
     )
 
     create_res = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding.id,
                 "classifications": [
                     {
                         "classification": classification.id,
                         "choice": invalid_choice.id,
                     }
                 ],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert create_res.status_code == 400, create_res.content.decode()
     payload = create_res.json()
     assert payload.get("code") == "invalid-choice"
 
 
 def test_base_api_patient_findings_validation_uses_examination_pinned_legacy_kb(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     client = Client()
     user_model = get_user_model()
     user = user_model.objects.create(
         username="legacy-kb-user",
         is_staff=True,
     )
     client.force_login(user)
     examination, finding, classification, choice = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
     patient_examination.knowledge_base_module = "catalog_module"
     patient_examination.knowledge_base_version = "1.0.0"
     patient_examination.save(
         update_fields=["knowledge_base_module", "knowledge_base_version"]
     )
 
     monkeypatch.setattr(findings_routes, "_kb_lookup", _mock_kb_lookup)
 
     create_res = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding.id,
                 "classifications": [
                     {"classification": classification.id, "choice": choice.id}
                 ],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert create_res.status_code == 200, create_res.content.decode()
 
 
 def test_base_api_patient_findings_rejects_kb_choice_not_in_pinned_legacy_kb(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     client = Client()
     user_model = get_user_model()
     user = user_model.objects.create(
         username="legacy-kb-user-2",
         is_staff=True,
     )
     client.force_login(user)
     examination, finding, _classification, _choice = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
     patient_examination.knowledge_base_module = "catalog_module"
     patient_examination.knowledge_base_version = "1.0.0"
     patient_examination.save(
         update_fields=["knowledge_base_module", "knowledge_base_version"]
     )
 
     forbidden_classification = FindingClassification.objects.create(
         name="forbidden_classification",
         description="Not in legacy KB",
     )
     forbidden_choice = FindingClassificationChoice.objects.create(
         name="forbidden_choice",
         description="Not in legacy KB",
         subcategories={},
         numerical_descriptors={},
     )
     forbidden_classification.choices.add(forbidden_choice)
     finding.finding_classifications.add(forbidden_classification)
 
     monkeypatch.setattr(findings_routes, "_kb_lookup", _mock_kb_lookup)
 
     create_res = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding.id,
                 "classifications": [
                     {
                         "classification": forbidden_classification.id,
                         "choice": forbidden_choice.id,
                     }
                 ],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert create_res.status_code == 400, create_res.content.decode()
     payload = create_res.json()
     assert payload.get("code") == "invalid-choice"
 
 
 def test_base_api_patient_findings_validation_duplicate_finding() -> None:
     client = Client()
     examination, finding, _classification, _choice = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
 
     first_res = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding.id,
                 "classifications": [],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert first_res.status_code == 200, first_res.content.decode()
 
     duplicate_res = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": finding.id,
                 "classifications": [],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert duplicate_res.status_code == 400
     payload = duplicate_res.json()
     assert payload.get("code") == "duplicate-finding"
 
 
 def test_base_api_patient_findings_validation_invalid_finding_for_examination() -> None:
     client = Client()
     examination, _finding, _classification, _choice = _create_exam_graph()
     patient_examination = _create_patient_examination(examination)
     unrelated_finding = Finding.objects.create(name="unrelated_finding")
 
     create_res = client.post(
         "/base_api/patient-findings/",
         data=json.dumps(
             {
                 "patient_examination": patient_examination.id,
                 "finding": unrelated_finding.id,
                 "classifications": [],
             }
         ),
         content_type="application/json",
         secure=True,
     )
     assert create_res.status_code == 400
     payload = create_res.json()
     assert payload.get("code") == "invalid-finding"
diff --git lx_dtypes/django/models.py lx_dtypes/django/models.py
index 6393a07..7fb4c3c 100644
--- lx_dtypes/django/models.py
+++ lx_dtypes/django/models.py
@@ -1,101 +1,103 @@
 from lx_dtypes.models.knowledge_base.citation.CitationDjango import CitationDjango
 from lx_dtypes.models.knowledge_base.classification._ClassificationDjango import (
     ClassificationDjango,
 )
 from lx_dtypes.models.knowledge_base.classification._ClassificationTypeDjango import (
     ClassificationTypeDjango,
 )
 from lx_dtypes.models.knowledge_base.classification_choice.ClassificationChoiceDjango import (
     ClassificationChoiceDjango,
 )
 from lx_dtypes.models.knowledge_base.classification_choice_descriptor.ClassificationChoiceDescriptorDjango import (
     ClassificationChoiceDescriptorDjango,
 )
 from lx_dtypes.models.knowledge_base.examination.ExaminationDjango import (
     ExaminationDjango,
 )
 from lx_dtypes.models.knowledge_base.examination.ExaminationTypeDjango import (
     ExaminationTypeDjango,
 )
 from lx_dtypes.models.knowledge_base.finding._FindingDjango import FindingDjango
 from lx_dtypes.models.knowledge_base.finding._FindingTypeDjango import (
     FindingTypeDjango,
 )
 from lx_dtypes.models.knowledge_base.indication.IndicationDjango import (
     IndicationDjango,
 )
 from lx_dtypes.models.knowledge_base.indication.IndicationTypeDjango import (
     IndicationTypeDjango,
 )
 from lx_dtypes.models.knowledge_base.information_source.InformationSourceDjango import (
     InformationSourceDjango,
 )
 from lx_dtypes.models.knowledge_base.information_source.InformationSourceTypeDjango import (
     InformationSourceTypeDjango,
 )
 from lx_dtypes.models.knowledge_base.intervention.InterventionDjango import (
     InterventionDjango,
 )
 from lx_dtypes.models.knowledge_base.intervention.InterventionTypeDjango import (
     InterventionTypeDjango,
 )
 from lx_dtypes.models.knowledge_base.unit.UnitDjango import UnitDjango
 from lx_dtypes.models.knowledge_base.unit.UnitTypeDjango import UnitTypeDjango
 from lx_dtypes.models.ledger.center.Django import CenterDjango
 from lx_dtypes.models.ledger.examiner.Django import ExaminerDjango
 from lx_dtypes.models.ledger.p_examination.Django import PExaminationDjango
 from lx_dtypes.models.ledger.p_finding.Django import PFindingDjango
 from lx_dtypes.models.ledger.p_finding_classification_choice.Django import (
     PFindingClassificationChoiceDjango,
 )
 from lx_dtypes.models.ledger.p_finding_classification_choice_descriptor.Django import (
     PFindingClassificationChoiceDescriptorDjango,
 )
 from lx_dtypes.models.ledger.p_finding_classifications.Django import (
     PFindingClassificationsDjango,
 )
 from lx_dtypes.models.ledger.p_indication.Django import PIndicationDjango
 from lx_dtypes.models.ledger.p_indication_classification.Django import (
     PIndicationClassificationDjango,
 )
 from lx_dtypes.models.ledger.p_indication_classification_descriptor.Django import (
     PIndicationClassificationDescriptorDjango,
 )
 from lx_dtypes.models.ledger.p_intervention.Django import PFindingInterventionDjango
 from lx_dtypes.models.ledger.p_interventions.Django import (
     PFindingInterventionsDjango,
 )
+from lx_dtypes.models.ledger.video_file.Django import VideoFileDjango
 from lx_dtypes.models.ledger.patient.Django import PatientDjango
 
 __all__ = [
     "CenterDjango",
     "CitationDjango",
     "ClassificationChoiceDescriptorDjango",
     "ClassificationChoiceDjango",
     "ClassificationDjango",
     "ClassificationTypeDjango",
     "ExaminationDjango",
     "ExaminationTypeDjango",
     "ExaminerDjango",
     "FindingDjango",
     "FindingTypeDjango",
     "IndicationDjango",
     "IndicationTypeDjango",
     "InformationSourceDjango",
     "InformationSourceTypeDjango",
     "InterventionDjango",
     "InterventionTypeDjango",
     "PExaminationDjango",
     "PFindingClassificationChoiceDescriptorDjango",
     "PFindingClassificationChoiceDjango",
     "PFindingClassificationsDjango",
     "PFindingDjango",
     "PFindingInterventionDjango",
     "PFindingInterventionsDjango",
+    "VideoFileDjango",
     "PatientDjango",
     "PIndicationDjango",
     "PIndicationClassificationDjango",
     "PIndicationClassificationDescriptorDjango",
     "UnitDjango",
     "UnitTypeDjango",
 ]
diff --git lx_dtypes/models/contracts/__init__.py lx_dtypes/models/contracts/__init__.py
index 70a8d8c..0fe1e2a 100644
--- lx_dtypes/models/contracts/__init__.py
+++ lx_dtypes/models/contracts/__init__.py
@@ -1,967 +1,1013 @@
 from .adapters import (
     CoreConceptModel,
     CoreConceptName,
     canonical_payload_to_storage,
     core_concept_to_storage,
     kb_to_core_concepts_payload,
     record_to_core_concept,
     records_to_core_concepts,
 )
 from .case_resolution import (
     CaseResolutionNewPatient,
     CaseResolutionRequest,
     CaseResolutionResponse,
     ValidationError,
 )
 from .codemod_rename import CodemodRenameMapPayload, validate_codemod_rename_map
 from .django_settings import (
     DjangoBeatScheduleEntryPayload,
     DjangoBeatScheduleOptionsPayload,
     DjangoCacheConfigPayload,
     DjangoCacheSettingsPayload,
     DjangoRestFrameworkSettingsPayload,
     DjangoTemplateConfigPayload,
     DjangoTemplateOptionsPayload,
 )
 from .contraindication import ContraindicationCore
 from .event import (
     EventClassificationChoiceCore,
     EventClassificationCore,
     EventCore,
 )
 from .fhir_clinical import (
     ClinicalFhirResource,
     FhirClinicalBundle,
     FhirClinicalBundleEntry,
     FhirCodeableConcept,
     FhirCoding,
     FhirCondition,
     FhirDiagnosticReport,
     FhirObservation,
     FhirObservationComponent,
     FhirPatient,
     FhirQuantity,
     FhirReference,
     ResolvedDiagnosticReport,
 )
 from .finding_classification import (
     FindingClassificationChoiceCore,
     FindingClassificationCore,
     FindingClassificationTypeCore,
 )
 from .finding_intervention import (
     FindingInterventionCore,
     FindingInterventionTypeCore,
 )
 from .examination_indication import (
     ExaminationIndicationClassificationChoiceCore,
     ExaminationIndicationClassificationCore,
     ExaminationIndicationCore,
 )
 from .examination_time import ExaminationTimeCore, ExaminationTimeTypeCore
 from .examination_type import ExaminationTypeCore
 from .endoscopy_processor import (
     EndoscopeImageRoiCore,
     EndoscopyProcessorCore,
     RoiBoxCore,
 )
 from .authz import (
     KeycloakClaimsPayload,
     KeycloakRoleContainerPayload,
     validate_keycloak_claims,
 )
 from .ai_model import (
     AiModelSerializerInputPayload,
     AiModelSerializerOutputPayload,
     validate_ai_model_serializer_input_payload,
     validate_ai_model_serializer_output_payload,
 )
 from .anonymization_metrics import (
     AnonymizationFieldQualityPayload,
     AnonymizationMetricsFiltersPayload,
     AnonymizationMetricsPayload,
     AnonymizationMetricsQueryBoundsPayload,
     AnonymizationPhiRegionMetricsPayload,
     AnonymizationQualityMetricsPayload,
     AnonymizationWorkflowMetricsPayload,
 )
 from .anonymization_quality import (
     AnonymizationQualityPayload,
     AnonymizationQualityResult,
     AnonymizationQualitySummary,
     QualityEvaluationStatus,
     SensitiveMetaHandlingPolicy,
 )
 from .application_settings import (
     ApplicationSettingsBackupSourcePayload,
     ApplicationSettingsBackupStatusPayload,
     ApplicationSettingsDataSetEntryPayload,
     ApplicationSettingsDeploymentProfilePayload,
     ApplicationSettingsDeploymentRole,
     ApplicationSettingsPayload,
 )
 from .aidataset_frame_buckets import (
     AIDataSetFrameBucketCount,
     AIDataSetFrameBucketDistribution,
     AIDataSetFrameBucketSummary,
     AIDataSetLabelDistributionEntry,
     AIDataSetLabelFrameBucketCount,
     AIDataSetTargetFrameBucket,
 )
 from .core_concepts import (
     CitationCore,
     ClassificationChoiceCore,
     ClassificationChoiceDescriptorCore,
     ClassificationCore,
     CoreConceptBase,
     CoreConceptCollection,
     ExaminationCore,
     FindingCore,
     FindingTypeCore,
     IndicationCore,
     IndicationTypeCore,
     InformationSourceCore,
     InformationSourceTypeCore,
     InterventionCore,
     InterventionTypeCore,
     UnitCore,
     UnitTypeCore,
 )
 from .ai_dataset import (
     AIDataSetActiveLearningCandidateContract,
     AIDataSetActiveLearningConfigContract,
     AIDataSetActiveLearningSelectionContract,
     AIDataSetScoredActiveLearningCandidateContract,
     AIDataSetStandardExportScopeContract,
 )
 from .document_type import DocumentType
 from .dtypes_record_persistence import (
     DescriptorValue,
     DtypesRecordClassificationChoiceDescriptorPayload,
     DtypesRecordClassificationChoicePayload,
     DtypesRecordClassificationGroupPayload,
     DtypesRecordFindingPayload,
     DtypesRecordIndicationClassificationDescriptorPayload,
     DtypesRecordIndicationClassificationPayload,
     DtypesRecordIndicationPayload,
     DtypesRecordInterventionGroupPayload,
     DtypesRecordInterventionPayload,
     DtypesRecordPersistencePayload,
     dump_dtypes_record_persistence_payload,
     parse_dtypes_record_persistence_payload,
 )
 from .lab_value import (
     LabValueNormalRangeData,
     LabValueNormalRangePayload,
 )
 from .knowledge_base import KnowledgeBaseContract
 from .nginx_accel import NginxAccelResponseHeadersPayload
 from .huggingface_model_meta import (
     HuggingFaceModelMetaCommandData,
     HuggingFaceModelMetaCommandPayload,
     HuggingFaceModelMetaCommandValue,
     huggingface_model_meta_command_data_from_mapping,
     validate_huggingface_model_meta_command_payload,
 )
 from .legacy_data_import import (
     LegacyDataImportCommandOptionsPayload,
     LegacyExaminationIdValue,
     LegacyImageImportRowPayload,
     LegacyImportManifestData,
     LegacyImportManifestPayload,
     LegacyIntOrNull,
     LegacyTextOrNull,
     NullValue as LegacyNullValue,
     dump_legacy_import_manifest,
 )
 from .lx_anonymizer_performance import (
     LX_ANONYMIZER_PERFORMANCE_CSV_FIELDNAMES,
     LxAnonymizerDurationStatsPayload,
     LxAnonymizerPerformanceCsvCell,
     LxAnonymizerPerformanceCsvRow,
     LxAnonymizerPerformanceMediaType,
     LxAnonymizerPerformancePayload,
     LxAnonymizerPerformanceRunPayload,
     LxAnonymizerPerformanceSummaryPayload,
     dump_lx_anonymizer_performance_run_csv_row,
 )
 from .maintenance_repair import (
     SensitiveMetaPatientRepairCreatePayload,
     SensitiveMetaPatientRepairData,
     SensitiveMetaPatientRepairUpdatePayload,
     SensitiveMetaRepairValue,
     VideoPathRepairFileIndex,
     VideoPathRepairFileInfoPayload,
     dump_sensitive_meta_patient_repair_create_payload,
     dump_sensitive_meta_patient_repair_update_payload,
 )
 from .management_command import (
     FrameSegmentReconciliationTrack,
     ModelInputCommandOptionsPayload,
     ModelTrainingResultPayload,
     ReconcileFrameSegmentAnnotationsCommandOptionsPayload,
     ReconcileMediaIntegrityCommandOptionsPayload,
     ReconcileSegmentValidationStateCommandOptionsPayload,
     ReconcileVideoFormatsCommandOptionsPayload,
     RefreshAuditLedgerIntegrityCommandOptionsPayload,
     RegisterAiModelCommandOptionsPayload,
     RegisterAiModelMetaPayload,
     ReapQuarantineCommandOptionsPayload,
     ReapUploadJobSourcesCommandOptionsPayload,
     SetupEndoregDbCommandOptionsPayload,
     ShowUrlsCommandOptionsPayload,
     ShowUrlsRoutePayload,
     ShowUrlsRoutesPayload,
     StorageManagementCommandOptionsPayload,
     StorageManagementInfoPayload,
     TrainImageMultilabelModelCommandOptionsPayload,
     TrainPhiRegionDetectorCommandOptionsPayload,
     TranscodeVideoCommandOptionsPayload,
     TranscodeVideoQualityMode,
     RuntimeStorageContractPayload,
     ValidateRuntimeStorageContractCommandOptionsPayload,
     ValidateVideoFileStatus,
     ValidateVideoFileStatusPayload,
     ValidateVideoFilesCommandOptionsPayload,
     VerboseManagementCommandOptionsPayload,
     validate_model_training_result,
 )
 from .migrate_data_dir import (
     MigrateDataDirCommandOptionsPayload,
     MigrateDataDirManifestData,
     MigrateDataDirManifestEntryData,
     MigrateDataDirManifestEntryPayload,
     MigrateDataDirManifestPayload,
     MigrateDataDirManifestValue,
     MigrateDataDirNull,
 )
 from .media_streaming import (
     ByteRange,
     FfmpegActiveStreamThrottleState,
     FfmpegStreamInfo,
     FfmpegStreamProbeEntry,
     FfmpegStreamThrottleState,
     FfmpegStreamThrottleStatePayload,
     MediaOperationLeaseSummary,
     MediaOperationLeaseSummaryPayload,
     MediaStreamDisposition,
     MediaStreamFileKind,
     StreamThrottleMode,
     dump_ffmpeg_stream_throttle_state,
     dump_media_operation_lease_summary,
     validate_ffmpeg_stream_info,
 )
 from .pdf_redaction import (
     PdfRedactionBox,
     PdfRedactionManifest,
     PdfRedactionPage,
     PdfRedactionRequest,
     PdfRedactionResponse,
 )
 from .pseudonymization import (
     KAnonymityResult,
     KPseudonymizationResult,
     QuasiIdentifierField,
     QuasiIdentifierSubset,
 )
 from .upload import (
     UploadApiRequestData,
     UploadApiRequestPayload,
     upload_api_request_data_from_mapping,
     validate_upload_api_request_payload,
 )
 from .patient_finding_classification import (
     PatientFindingClassificationCreateData,
     PatientFindingClassificationCreatePayload,
     dump_patient_finding_classification_create_payload,
     validate_patient_finding_classification_create_payload,
 )
 from .patient_finding_classification_runtime import (
     PatientFindingClassificationNumericalDescriptorPayload,
     PatientFindingClassificationNumericalDescriptorsData,
     PatientFindingClassificationNumericalDescriptorsPayload,
     PatientFindingClassificationSubcategoryPayload,
     PatientFindingClassificationSubcategoriesData,
     PatientFindingClassificationSubcategoriesPayload,
 )
 from .patient_examination_report import (
     PatientExaminationReportMakeReportData,
     PatientExaminationReportMakeReportPayload,
     PatientExaminationReportSubmissionData,
     PatientExaminationReportSubmissionPayload,
     PatientReportIdentityData,
     PatientReportIdentityPayload,
     ReportExportFrameDetailData,
     ReportJsonObject,
     ReportJsonValue,
     ReportPersistedArtifactsData,
     ReportPersistedArtifactsPayload,
     ReportSegmentFrameSelectionData,
     ReportSegmentFrameSelectionPayload,
     ReportSegmentSelectionMap,
     ReportStatus,
     SegmentAttachedFindingData,
     SegmentFrameControlsData,
     SegmentFramePreviewData,
     SegmentFrameSelectionAction,
     SegmentFrameSelectorItemData,
     SegmentFrameSelectorPatchData,
     SegmentFrameSelectorPatchPayload,
     SegmentFrameSelectorQueryData,
     SegmentFrameSelectorQueryPayload,
     SegmentFrameSelectorResponseData,
     SegmentSelectionMetaData,
     dump_make_report_payload,
     dump_persisted_artifacts_payload,
     dump_report_submission_payload,
     dump_segment_frame_selection_payload,
     dump_selector_patch_payload,
     dump_selector_query_payload,
     report_json_safe,
     report_json_safe_dict,
     validate_segment_selection_map,
 )
+from .report import (
+    Report,
+    ReportMetaJsonObject,
+    ReportMetaJsonValue,
+    ReportPayload,
+    ReportVersion,
+    SerializedReport,
+)
 from .patient_examination import PatientExaminationPatientDataPayload
 from .pdf_meta import PdfMetaPayload, PdfTypeSummaryPayload
+from .pdf_file import (
+    PdfFileContextPayload,
+    PdfFileIdentityPayload,
+    PdfFileMetaJsonObject,
+    PdfFileMetaJsonValue,
+    PdfFilePayload,
+    PdfFileProcessingStatePayload,
+    PdfFileStoragePayload,
+)
 from .transfer_validation import (
     TransferValidationFailureLogPayload,
     TransferValidationLogScalar,
     TransferValidationLogValue,
     dump_transfer_validation_failure_log_payload,
 )
 from .report_anonymization import (
     REPORT_ANONYMIZATION_CONTRACT_VERSION,
     ReportAnonymizationContractVersion,
     ReportAnonymizationErrorCode,
     ReportAnonymizationFailureV2,
     ReportAnonymizationOptions,
     ReportAnonymizationPhase,
     ReportAnonymizationProvenanceV2,
     ReportAnonymizationRequestV2,
     ReportAnonymizationResult,
     ReportAnonymizationResultV2,
     ReportAnonymizationWarningCode,
     ReportAnonymizationWarningV2,
     ReportArtifactValidationV2,
 )
 from .report_context import ReportContext
 from .video_ai_labels import (
     VideoAiHuggingFaceModelPayload,
     VideoAiJsonObject,
     VideoAiLabelNamePayload,
     VideoAiLabelMutationResponsePayload,
     VideoAiLabelPayload,
     VideoAiLabelRenamePayload,
     VideoAiLabelSetPayload,
     VideoAiPredictionJobPayload,
     VideoAiPredictionModelListPayload,
     VideoAiPredictionModelMetaPayload,
     VideoAiRerunPredictionRequestPayload,
     VideoAiRerunPredictionResponsePayload,
     validate_video_ai_label_name_payload,
     validate_video_ai_label_rename_payload,
     validate_video_ai_rerun_prediction_request,
     video_ai_json_safe_dict,
 )
 from .video_segments import (
     VideoSegmentsPayload,
     VideoSegmentsPayloadDict,
     SegmentAnnotationEnsurePayload,
     SegmentAnnotationInput,
     SegmentAnnotationMetadataInput,
     SegmentBlackenOutsidePayload,
     SegmentBulkValidationItem,
     SegmentBulkValidationPayload,
     SegmentCrudPayload,
     SegmentListQuery,
     SegmentPredictionImportItem,
     SegmentPredictionImportPayload,
     SegmentValidationPayload,
     SegmentValidationStatusPayload,
     parse_segment_annotation_input,
     validate_segment_annotation_ensure_payload,
     validate_segment_blacken_outside_payload,
     validate_segment_bulk_validation_payload,
     validate_segment_crud_payload,
     validate_segment_list_query,
     validate_segment_prediction_import_payload,
     validate_video_segments_payload,
     validate_segment_validation_payload,
     validate_segment_validation_status_payload,
 )
 from .video_examination import (
     VideoExaminationCreateData,
     VideoExaminationCreatePayload,
     VideoExaminationFindingData,
     VideoExaminationFindingPayload,
     VideoExaminationListQueryData,
     VideoExaminationListQueryPayload,
     VideoExaminationPathPayload,
     VideoExaminationUpdateData,
     VideoExaminationUpdatePayload,
     dump_video_examination_create_payload,
     dump_video_examination_finding_payload,
     dump_video_examination_list_query_payload,
     dump_video_examination_update_payload,
     validate_video_examination_list_query,
     validate_video_examination_path_payload,
 )
+from .video_file import (
+    FrameSourceMode,
+    VideoFileIdentityPayload,
+    VideoFileMetaJsonObject,
+    VideoFileMetaJsonValue,
+    VideoFilePayload,
+    VideoFileStoragePayload,
+    VideoFileTechnicalMetadataPayload,
+)
 from .video_frame_box_annotations import (
     VideoFrameBoxAnnotationListResponsePayload,
     VideoFrameBoxAnnotationMutationResponsePayload,
     VideoFrameBoxAnnotationRequestPayload,
     VideoFrameBoxJsonObject,
     validate_video_frame_box_annotation_request,
     video_frame_box_json_safe_dict,
 )
 from .video_segment_validation import (
     OutsideFrameBlackeningHistoryConfigData,
     OutsideFrameBlackeningHistoryConfigPayload,
     PostValidationRebuildSummaryData,
     PostValidationRebuildSummaryPayload,
     VideoSegmentValidationNull,
     VideoSegmentValidationText,
 )
 from .video_correction import (
     VideoCorrectionApplyMaskPayload,
     VideoCorrectionApplyMaskResponsePayload,
     VideoCorrectionErrorPayload,
     VideoCorrectionFrameRemovalPayload,
     VideoCorrectionMaskType,
     VideoCorrectionProcessingMethod,
     VideoCorrectionRemoveFramesResponsePayload,
     VideoCorrectionRoiData,
     VideoCorrectionRoiPayload,
     VideoCorrectionSegmentUpdateData,
     VideoCorrectionSegmentUpdatePayload,
     dump_video_correction_roi_payload,
     dump_video_correction_segment_update_payload,
     parse_video_correction_frame_ranges,
     validate_video_correction_apply_mask_payload,
     validate_video_correction_frame_removal_payload,
 )
 from .video_export import (
     VideoAnnotationExportConfigUpdateData,
     VideoAnnotationExportErrorPayload,
     VideoAnnotationExportFormat,
     VideoAnnotationExportRequestPayload,
     VideoAnnotationExportResultPayload,
     dump_video_annotation_export_update_payload,
 )
 from .export_ready import (
     ReadyForExportResult,
     VideoReadyForExportData,
     VideoReadyForExportPayload,
     dump_video_ready_for_export_payload,
     validate_video_ready_for_export_payload,
 )
 from .video_frame_export import (
     VideoFrameAnnotationExportConfigPayload,
     VideoFrameAnnotationExportFormat,
     YamlScalar,
     YamlValue,
     export_config,
     export_result,
     load_video_frame_annotation_export_config,
     validate_video_frame_annotation_export_config,
 )
 from .video_temporal_inference import (
     TemporalInferenceDispatchResult,
     TemporalInferenceHistoryConfigPayload,
     TemporalInferenceHistoryResultPayload,
     parse_temporal_inference_history_config_payload,
     parse_temporal_inference_history_result_payload,
 )
 from .frame_annotation import (
     FrameAnnotationRandomTaskResponsePayload,
     FrameAnnotationSkipResponsePayload,
 )
 from .video_frame_annotations import (
     FrameAnnotationBulkEnvelopeData,
     FrameAnnotationBulkEnvelopePayload,
     FrameAnnotationBulkItemData,
     FrameAnnotationBulkItemPayload,
     FrameAnnotationPayloadMapping,
     FrameAnnotationSkipPayload,
     FrameBoxAnnotationBulkEnvelopeData,
     FrameBoxAnnotationBulkEnvelopePayload,
     FrameBoxAnnotationBulkItemData,
     FrameBoxAnnotationBulkItemPayload,
     dump_frame_annotation_bulk_item,
     dump_frame_box_annotation_bulk_item,
 )
 from .video_text_metadata import VideoTextMetaPayload, VideoTextMetaValue
 from .hub_transfer import (
     HubTransferReportTransferPayload,
     HubTransferSegmentProvenancePayload,
     HubTransferVideoSegmentPayload,
     HubTransferVideoTransferPayload,
     validate_hub_transfer_report_payload,
     validate_hub_transfer_video_payload,
 )
 from .video_reimport import (
     JsonObject,
     JsonValue,
     VIDEO_REIMPORT_HISTORY_KIND,
     VIDEO_REIMPORT_OPERATION,
     VideoReimportApiResponseData,
     VideoReimportApiResponsePayload,
     VideoReimportApiStatus,
     VideoReimportDispatchResult,
     VideoReimportDispatchStatus,
     VideoReimportErrorType,
     VideoReimportHistoryConfig,
     VideoReimportJobMode,
     VideoReimportJsonValue,
     VideoReimportOperation,
     VideoReimportPredictionRefreshPayload,
     VideoReimportPredictionRefreshStatus,
     VideoReimportRequestData,
     VideoReimportRequestPayload,
     VideoReimportStatus,
     dump_video_reimport_api_response,
     dump_video_reimport_request_payload,
     validate_video_reimport_request_payload,
     video_reimport_json_safe,
     video_reimport_json_safe_dict,
 )
 
 __all__ = [
     "ClinicalFhirResource",
     "FhirClinicalBundle",
     "FhirClinicalBundleEntry",
     "FhirCodeableConcept",
     "FhirCoding",
     "FhirCondition",
     "FhirDiagnosticReport",
     "FhirObservation",
     "FhirObservationComponent",
     "FhirPatient",
     "FhirQuantity",
     "FhirReference",
     "ResolvedDiagnosticReport",
     "ApplicationSettingsBackupSourcePayload",
     "ApplicationSettingsBackupStatusPayload",
     "ApplicationSettingsDataSetEntryPayload",
     "ApplicationSettingsDeploymentProfilePayload",
     "ApplicationSettingsDeploymentRole",
     "ApplicationSettingsPayload",
     "AIDataSetActiveLearningCandidateContract",
     "AIDataSetActiveLearningConfigContract",
     "AIDataSetActiveLearningSelectionContract",
     "AIDataSetScoredActiveLearningCandidateContract",
     "AIDataSetStandardExportScopeContract",
     "DjangoBeatScheduleEntryPayload",
     "DjangoBeatScheduleOptionsPayload",
     "DjangoCacheConfigPayload",
     "DjangoCacheSettingsPayload",
     "DjangoRestFrameworkSettingsPayload",
     "DjangoTemplateConfigPayload",
     "DjangoTemplateOptionsPayload",
     "DtypesRecordClassificationChoicePayload",
     "DtypesRecordClassificationChoiceDescriptorPayload",
     "DtypesRecordClassificationGroupPayload",
     "DtypesRecordFindingPayload",
     "DtypesRecordIndicationClassificationDescriptorPayload",
     "DtypesRecordIndicationClassificationPayload",
     "DtypesRecordIndicationPayload",
     "DtypesRecordInterventionGroupPayload",
     "DtypesRecordInterventionPayload",
     "DtypesRecordPersistencePayload",
     "dump_dtypes_record_persistence_payload",
     "parse_dtypes_record_persistence_payload",
     "DescriptorValue",
     "HubTransferReportTransferPayload",
     "HubTransferSegmentProvenancePayload",
     "HubTransferVideoSegmentPayload",
     "HubTransferVideoTransferPayload",
     "validate_hub_transfer_report_payload",
     "validate_hub_transfer_video_payload",
     "CoreConceptBase",
     "ClassificationCore",
     "ClassificationChoiceCore",
     "ClassificationChoiceDescriptorCore",
     "ExaminationCore",
     "FindingCore",
     "FindingTypeCore",
     "IndicationCore",
     "IndicationTypeCore",
     "InterventionCore",
     "InterventionTypeCore",
     "UnitCore",
     "UnitTypeCore",
     "InformationSourceCore",
     "InformationSourceTypeCore",
     "CitationCore",
     "CoreConceptCollection",
     "ContraindicationCore",
     "EventCore",
     "EventClassificationCore",
     "EventClassificationChoiceCore",
     "FindingClassificationTypeCore",
     "FindingClassificationCore",
     "FindingClassificationChoiceCore",
     "FindingInterventionCore",
     "FindingInterventionTypeCore",
     "ExaminationIndicationCore",
     "ExaminationIndicationClassificationCore",
     "ExaminationIndicationClassificationChoiceCore",
     "ExaminationTimeCore",
     "ExaminationTimeTypeCore",
     "ExaminationTypeCore",
     "RoiBoxCore",
     "EndoscopeImageRoiCore",
     "EndoscopyProcessorCore",
     "CoreConceptName",
     "CoreConceptModel",
     "record_to_core_concept",
     "records_to_core_concepts",
     "core_concept_to_storage",
     "kb_to_core_concepts_payload",
     "canonical_payload_to_storage",
     "CaseResolutionNewPatient",
     "CaseResolutionRequest",
     "CaseResolutionResponse",
     "ValidationError",
     "CodemodRenameMapPayload",
     "validate_codemod_rename_map",
     "AnonymizationFieldQualityPayload",
     "AnonymizationMetricsFiltersPayload",
     "AnonymizationMetricsPayload",
     "AnonymizationMetricsQueryBoundsPayload",
     "AnonymizationPhiRegionMetricsPayload",
     "AnonymizationQualityMetricsPayload",
     "AnonymizationWorkflowMetricsPayload",
     "AnonymizationQualityPayload",
     "AnonymizationQualityResult",
     "AnonymizationQualitySummary",
     "QualityEvaluationStatus",
     "SensitiveMetaHandlingPolicy",
     "AIDataSetFrameBucketCount",
     "AIDataSetFrameBucketDistribution",
     "AIDataSetFrameBucketSummary",
     "AIDataSetLabelDistributionEntry",
     "AIDataSetLabelFrameBucketCount",
     "AIDataSetTargetFrameBucket",
     "LabValueNormalRangeData",
     "LabValueNormalRangePayload",
     "KnowledgeBaseContract",
     "NginxAccelResponseHeadersPayload",
     "KeycloakClaimsPayload",
     "AiModelSerializerInputPayload",
     "AiModelSerializerOutputPayload",
     "validate_ai_model_serializer_input_payload",
     "validate_ai_model_serializer_output_payload",
     "KeycloakRoleContainerPayload",
     "validate_keycloak_claims",
     "DocumentType",
     "HuggingFaceModelMetaCommandData",
     "HuggingFaceModelMetaCommandPayload",
     "HuggingFaceModelMetaCommandValue",
     "huggingface_model_meta_command_data_from_mapping",
     "validate_huggingface_model_meta_command_payload",
     "LegacyDataImportCommandOptionsPayload",
     "LegacyExaminationIdValue",
     "LegacyImageImportRowPayload",
     "LegacyImportManifestData",
     "LegacyImportManifestPayload",
     "LegacyIntOrNull",
     "LegacyNullValue",
     "LegacyTextOrNull",
     "dump_legacy_import_manifest",
     "LX_ANONYMIZER_PERFORMANCE_CSV_FIELDNAMES",
     "LxAnonymizerDurationStatsPayload",
     "LxAnonymizerPerformanceCsvCell",
     "LxAnonymizerPerformanceCsvRow",
     "LxAnonymizerPerformanceMediaType",
     "LxAnonymizerPerformancePayload",
     "LxAnonymizerPerformanceRunPayload",
     "LxAnonymizerPerformanceSummaryPayload",
     "dump_lx_anonymizer_performance_run_csv_row",
     "SensitiveMetaPatientRepairCreatePayload",
     "SensitiveMetaPatientRepairData",
     "SensitiveMetaPatientRepairUpdatePayload",
     "SensitiveMetaRepairValue",
     "VideoPathRepairFileIndex",
     "VideoPathRepairFileInfoPayload",
     "dump_sensitive_meta_patient_repair_create_payload",
     "dump_sensitive_meta_patient_repair_update_payload",
     "VerboseManagementCommandOptionsPayload",
     "MigrateDataDirCommandOptionsPayload",
     "MigrateDataDirManifestData",
     "MigrateDataDirManifestEntryData",
     "MigrateDataDirManifestEntryPayload",
     "MigrateDataDirManifestPayload",
     "MigrateDataDirManifestValue",
     "MigrateDataDirNull",
     "FrameSegmentReconciliationTrack",
     "ModelInputCommandOptionsPayload",
     "ModelTrainingResultPayload",
     "ReconcileFrameSegmentAnnotationsCommandOptionsPayload",
     "ReconcileMediaIntegrityCommandOptionsPayload",
     "ReconcileSegmentValidationStateCommandOptionsPayload",
     "ReconcileVideoFormatsCommandOptionsPayload",
     "RefreshAuditLedgerIntegrityCommandOptionsPayload",
     "RegisterAiModelCommandOptionsPayload",
     "RegisterAiModelMetaPayload",
     "ReapQuarantineCommandOptionsPayload",
     "ReapUploadJobSourcesCommandOptionsPayload",
     "SetupEndoregDbCommandOptionsPayload",
     "ShowUrlsCommandOptionsPayload",
     "ShowUrlsRoutePayload",
     "ShowUrlsRoutesPayload",
     "StorageManagementCommandOptionsPayload",
     "StorageManagementInfoPayload",
     "TrainImageMultilabelModelCommandOptionsPayload",
     "TrainPhiRegionDetectorCommandOptionsPayload",
     "TranscodeVideoCommandOptionsPayload",
     "TranscodeVideoQualityMode",
     "RuntimeStorageContractPayload",
     "ValidateRuntimeStorageContractCommandOptionsPayload",
     "ValidateVideoFileStatus",
     "ValidateVideoFileStatusPayload",
     "ValidateVideoFilesCommandOptionsPayload",
     "validate_model_training_result",
     "validate_video_segments_payload",
     "VideoSegmentsPayloadDict",
     "ByteRange",
     "FfmpegActiveStreamThrottleState",
     "FfmpegStreamInfo",
     "FfmpegStreamProbeEntry",
     "FfmpegStreamThrottleState",
     "FfmpegStreamThrottleStatePayload",
     "MediaOperationLeaseSummary",
     "MediaOperationLeaseSummaryPayload",
     "MediaStreamDisposition",
     "MediaStreamFileKind",
     "StreamThrottleMode",
     "dump_ffmpeg_stream_throttle_state",
     "dump_media_operation_lease_summary",
     "validate_ffmpeg_stream_info",
     "KAnonymityResult",
     "KPseudonymizationResult",
     "QuasiIdentifierField",
     "QuasiIdentifierSubset",
     "PdfRedactionBox",
     "PdfRedactionManifest",
     "PdfRedactionPage",
     "PdfRedactionRequest",
     "PdfRedactionResponse",
     "UploadApiRequestPayload",
     "UploadApiRequestData",
     "upload_api_request_data_from_mapping",
     "validate_upload_api_request_payload",
     "PatientFindingClassificationCreateData",
     "PatientFindingClassificationCreatePayload",
     "dump_patient_finding_classification_create_payload",
     "PatientFindingClassificationNumericalDescriptorPayload",
     "PatientFindingClassificationNumericalDescriptorsData",
     "PatientFindingClassificationNumericalDescriptorsPayload",
     "PatientFindingClassificationSubcategoryPayload",
     "PatientFindingClassificationSubcategoriesData",
     "PatientFindingClassificationSubcategoriesPayload",
     "PdfMetaPayload",
     "PdfTypeSummaryPayload",
+    "PdfFileContextPayload",
+    "PdfFileIdentityPayload",
+    "PdfFileMetaJsonObject",
+    "PdfFileMetaJsonValue",
+    "PdfFilePayload",
+    "PdfFileProcessingStatePayload",
+    "PdfFileStoragePayload",
     "validate_patient_finding_classification_create_payload",
     "PatientExaminationReportMakeReportData",
     "PatientExaminationReportMakeReportPayload",
     "PatientExaminationReportSubmissionData",
     "PatientExaminationReportSubmissionPayload",
     "PatientExaminationPatientDataPayload",
     "PdfMetaPayload",
     "PdfTypeSummaryPayload",
     "TransferValidationFailureLogPayload",
     "TransferValidationLogScalar",
     "TransferValidationLogValue",
     "dump_transfer_validation_failure_log_payload",
     "PatientReportIdentityData",
     "PatientReportIdentityPayload",
     "ReportExportFrameDetailData",
     "ReportJsonObject",
     "ReportJsonValue",
+    "ReportMetaJsonObject",
+    "ReportMetaJsonValue",
     "ReportPersistedArtifactsData",
     "ReportPersistedArtifactsPayload",
     "ReportSegmentFrameSelectionData",
     "ReportSegmentFrameSelectionPayload",
     "ReportSegmentSelectionMap",
     "ReportStatus",
     "SegmentAttachedFindingData",
     "SegmentFrameControlsData",
     "SegmentFramePreviewData",
     "SegmentFrameSelectionAction",
     "SegmentFrameSelectorItemData",
     "SegmentFrameSelectorPatchData",
     "SegmentFrameSelectorPatchPayload",
     "SegmentFrameSelectorQueryData",
     "SegmentFrameSelectorQueryPayload",
     "SegmentFrameSelectorResponseData",
     "SegmentSelectionMetaData",
     "dump_make_report_payload",
     "dump_persisted_artifacts_payload",
     "dump_report_submission_payload",
     "dump_segment_frame_selection_payload",
     "dump_selector_patch_payload",
     "dump_selector_query_payload",
     "report_json_safe",
     "report_json_safe_dict",
     "validate_segment_selection_map",
+    "Report",
+    "ReportPayload",
+    "ReportVersion",
+    "SerializedReport",
     "ReportAnonymizationResult",
     "REPORT_ANONYMIZATION_CONTRACT_VERSION",
     "ReportAnonymizationContractVersion",
     "ReportAnonymizationErrorCode",
     "ReportAnonymizationFailureV2",
     "ReportAnonymizationOptions",
     "ReportAnonymizationPhase",
     "ReportAnonymizationProvenanceV2",
     "ReportAnonymizationRequestV2",
     "ReportAnonymizationResultV2",
     "ReportAnonymizationWarningCode",
     "ReportAnonymizationWarningV2",
     "ReportArtifactValidationV2",
     "ReportContext",
     "VideoAiHuggingFaceModelPayload",
     "VideoAiJsonObject",
     "VideoAiLabelNamePayload",
     "VideoAiLabelMutationResponsePayload",
     "VideoAiLabelPayload",
     "VideoAiLabelRenamePayload",
     "VideoAiLabelSetPayload",
     "VideoAiPredictionJobPayload",
     "VideoAiPredictionModelListPayload",
     "VideoAiPredictionModelMetaPayload",
     "VideoAiRerunPredictionRequestPayload",
     "VideoAiRerunPredictionResponsePayload",
     "validate_video_ai_label_name_payload",
     "validate_video_ai_label_rename_payload",
     "validate_video_ai_rerun_prediction_request",
     "video_ai_json_safe_dict",
     "SegmentAnnotationEnsurePayload",
     "SegmentAnnotationInput",
     "SegmentAnnotationMetadataInput",
     "SegmentBlackenOutsidePayload",
     "SegmentBulkValidationItem",
     "SegmentBulkValidationPayload",
     "SegmentCrudPayload",
     "SegmentListQuery",
     "SegmentPredictionImportItem",
     "SegmentPredictionImportPayload",
     "SegmentValidationPayload",
     "SegmentValidationStatusPayload",
     "parse_segment_annotation_input",
     "validate_segment_annotation_ensure_payload",
     "validate_segment_blacken_outside_payload",
     "validate_segment_bulk_validation_payload",
     "validate_segment_crud_payload",
     "validate_segment_list_query",
     "validate_segment_prediction_import_payload",
     "validate_segment_validation_payload",
     "validate_segment_validation_status_payload",
     "VideoExaminationCreateData",
     "VideoExaminationCreatePayload",
     "VideoExaminationFindingData",
     "VideoExaminationFindingPayload",
     "VideoExaminationListQueryData",
     "VideoExaminationListQueryPayload",
     "VideoExaminationPathPayload",
     "VideoExaminationUpdateData",
     "VideoExaminationUpdatePayload",
     "dump_video_examination_create_payload",
     "dump_video_examination_finding_payload",
     "dump_video_examination_list_query_payload",
     "dump_video_examination_update_payload",
     "validate_video_examination_list_query",
     "validate_video_examination_path_payload",
+    "FrameSourceMode",
+    "VideoFileIdentityPayload",
+    "VideoFileMetaJsonObject",
+    "VideoFileMetaJsonValue",
+    "VideoFilePayload",
+    "VideoFileStoragePayload",
+    "VideoFileTechnicalMetadataPayload",
     "VideoFrameBoxAnnotationListResponsePayload",
     "VideoFrameBoxAnnotationMutationResponsePayload",
     "VideoFrameBoxAnnotationRequestPayload",
     "VideoFrameBoxJsonObject",
     "validate_video_frame_box_annotation_request",
     "video_frame_box_json_safe_dict",
     "OutsideFrameBlackeningHistoryConfigData",
     "OutsideFrameBlackeningHistoryConfigPayload",
     "PostValidationRebuildSummaryData",
     "PostValidationRebuildSummaryPayload",
     "VideoSegmentValidationNull",
     "VideoSegmentValidationText",
     "VideoCorrectionApplyMaskPayload",
     "VideoCorrectionApplyMaskResponsePayload",
     "VideoCorrectionErrorPayload",
     "VideoCorrectionFrameRemovalPayload",
     "VideoCorrectionMaskType",
     "VideoCorrectionProcessingMethod",
     "VideoCorrectionRemoveFramesResponsePayload",
     "VideoCorrectionRoiData",
     "VideoCorrectionRoiPayload",
     "VideoCorrectionSegmentUpdateData",
     "VideoCorrectionSegmentUpdatePayload",
     "dump_video_correction_roi_payload",
     "dump_video_correction_segment_update_payload",
     "parse_video_correction_frame_ranges",
     "validate_video_correction_apply_mask_payload",
     "validate_video_correction_frame_removal_payload",
     "VideoAnnotationExportConfigUpdateData",
     "VideoAnnotationExportErrorPayload",
     "VideoAnnotationExportFormat",
     "VideoAnnotationExportRequestPayload",
     "VideoAnnotationExportResultPayload",
     "dump_video_annotation_export_update_payload",
     "ReadyForExportResult",
     "VideoReadyForExportData",
     "VideoReadyForExportPayload",
     "dump_video_ready_for_export_payload",
     "validate_video_ready_for_export_payload",
     "VideoFrameAnnotationExportConfigPayload",
     "VideoFrameAnnotationExportFormat",
     "YamlScalar",
     "YamlValue",
     "export_config",
     "export_result",
     "load_video_frame_annotation_export_config",
     "validate_video_frame_annotation_export_config",
     "TemporalInferenceDispatchResult",
     "TemporalInferenceHistoryConfigPayload",
     "TemporalInferenceHistoryResultPayload",
     "parse_temporal_inference_history_config_payload",
     "parse_temporal_inference_history_result_payload",
     "FrameAnnotationRandomTaskResponsePayload",
     "FrameAnnotationSkipResponsePayload",
     "FrameAnnotationBulkEnvelopeData",
     "FrameAnnotationBulkEnvelopePayload",
     "FrameAnnotationBulkItemData",
     "FrameAnnotationBulkItemPayload",
     "FrameAnnotationPayloadMapping",
     "FrameAnnotationSkipPayload",
     "FrameBoxAnnotationBulkEnvelopeData",
     "FrameBoxAnnotationBulkEnvelopePayload",
     "FrameBoxAnnotationBulkItemData",
     "FrameBoxAnnotationBulkItemPayload",
     "dump_frame_annotation_bulk_item",
     "dump_frame_box_annotation_bulk_item",
     "VideoTextMetaPayload",
     "VideoTextMetaValue",
     "VideoSegmentsPayload",
     "JsonObject",
     "JsonValue",
     "VIDEO_REIMPORT_HISTORY_KIND",
     "VIDEO_REIMPORT_OPERATION",
     "VideoReimportApiResponseData",
     "VideoReimportApiResponsePayload",
     "VideoReimportApiStatus",
     "VideoReimportDispatchResult",
     "VideoReimportDispatchStatus",
     "VideoReimportErrorType",
     "VideoReimportHistoryConfig",
     "VideoReimportJobMode",
     "VideoReimportJsonValue",
     "VideoReimportOperation",
     "VideoReimportPredictionRefreshPayload",
     "VideoReimportPredictionRefreshStatus",
     "VideoReimportRequestData",
     "VideoReimportRequestPayload",
     "VideoReimportStatus",
     "dump_video_reimport_api_response",
     "dump_video_reimport_request_payload",
     "validate_video_reimport_request_payload",
     "video_reimport_json_safe",
     "video_reimport_json_safe_dict",
 ]
diff --git lx_dtypes/models/contracts/adapters.py lx_dtypes/models/contracts/adapters.py
index fee3f18..8700379 100644
--- lx_dtypes/models/contracts/adapters.py
+++ lx_dtypes/models/contracts/adapters.py
@@ -1,372 +1,471 @@
 from __future__ import annotations
 
-from collections.abc import Iterable, Mapping
+from collections.abc import Iterable, Mapping, Sequence
 from typing import TYPE_CHECKING, Any, Literal, Protocol, TypeAlias, cast
 
+from lx_dtypes.models.contracts.json_types import JsonValue
+
 from lx_dtypes.serialization import parse_str_list, serialize_str_list
 
 from .core_concepts import (
     CitationCore,
     ClassificationChoiceCore,
     ClassificationChoiceDescriptorCore,
     ClassificationCore,
     CoreConceptCollection,
     ExaminationCore,
     FindingCore,
     FindingTypeCore,
     IndicationCore,
     IndicationTypeCore,
     InformationSourceCore,
     InformationSourceTypeCore,
     InterventionCore,
     InterventionTypeCore,
     UnitCore,
     UnitTypeCore,
 )
 
 if TYPE_CHECKING:
     from lx_dtypes.models.knowledge_base.citation.Citation import Citation
     from lx_dtypes.models.knowledge_base.classification.Classification import (
         Classification,
     )
     from lx_dtypes.models.knowledge_base.classification_choice.ClassificationChoice import (
         ClassificationChoice,
     )
     from lx_dtypes.models.knowledge_base.classification_choice_descriptor.ClassificationChoiceDescriptor import (
         ClassificationChoiceDescriptor,
     )
     from lx_dtypes.models.knowledge_base.examination.Examination import Examination
     from lx_dtypes.models.knowledge_base.finding._Finding import Finding
     from lx_dtypes.models.knowledge_base.finding._FindingType import FindingType
     from lx_dtypes.models.knowledge_base.indication.Indication import Indication
     from lx_dtypes.models.knowledge_base.indication.IndicationType import IndicationType
     from lx_dtypes.models.knowledge_base.information_source.InformationSource import (
         InformationSource,
     )
     from lx_dtypes.models.knowledge_base.information_source.InformationSourceType import (
         InformationSourceType,
     )
     from lx_dtypes.models.knowledge_base.intervention.Intervention import Intervention
     from lx_dtypes.models.knowledge_base.intervention.InterventionType import (
         InterventionType,
     )
     from lx_dtypes.models.knowledge_base.unit.Unit import Unit
     from lx_dtypes.models.knowledge_base.unit.UnitType import UnitType
 
     KnowledgeBaseCoreConceptModel: TypeAlias = (
         Classification
         | ClassificationChoice
         | ClassificationChoiceDescriptor
         | Examination
         | Finding
         | FindingType
         | Indication
         | IndicationType
         | Intervention
         | InterventionType
         | Unit
         | UnitType
         | InformationSource
         | InformationSourceType
         | Citation
     )
 else:
     KnowledgeBaseCoreConceptModel: TypeAlias = object
 
 CoreConceptName: TypeAlias = Literal[
     "classification",
     "classification_choice",
     "classification_choice_descriptor",
     "examination",
     "finding",
     "finding_type",
     "indication",
     "indication_type",
     "intervention",
     "intervention_type",
     "unit",
     "unit_type",
     "information_source",
     "information_source_type",
     "citation",
 ]
 
 CoreConceptModel: TypeAlias = (
     ClassificationCore
     | ClassificationChoiceCore
     | ClassificationChoiceDescriptorCore
     | ExaminationCore
     | FindingCore
     | FindingTypeCore
     | IndicationCore
     | IndicationTypeCore
     | InterventionCore
     | InterventionTypeCore
     | UnitCore
     | UnitTypeCore
     | InformationSourceCore
     | InformationSourceTypeCore
     | CitationCore
 )
 
-CoreConceptStorageRecord: TypeAlias = Mapping[str, Any] | KnowledgeBaseCoreConceptModel
+CoreConceptStorageRecord: TypeAlias = Mapping[str, JsonValue] | KnowledgeBaseCoreConceptModel
 
 
 class SupportsKnowledgeBaseListFields(Protocol):
     @classmethod
     def list_type_fields(cls) -> list[str]: ...
 
 
+class _KnowledgeBaseConfig(Protocol):
+    @property
+    def name(self) -> str:
+        ...
+
+
+class _KnowledgeBaseSection(Protocol):
+    def values(self) -> Iterable[CoreConceptStorageRecord]:
+        ...
+
+
+class _KnowledgeBaseLike(Protocol):
+    @property
+    def config(self) -> _KnowledgeBaseConfig | None:
+        ...
+
+    @property
+    def classification(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def classification_choice(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def classification_choice_descriptor(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def examination(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def finding(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def finding_type(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def indication(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def indication_type(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def intervention(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def intervention_type(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def unit(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def unit_type(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def information_source(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def information_source_type(self) -> Mapping[str, Any] | None:
+        ...
+
+    @property
+    def citation(self) -> Mapping[str, Any] | None:
+        ...
+
+
 _CONCEPT_MODEL_LOOKUP: dict[CoreConceptName, type[CoreConceptModel]] = {
     "classification": ClassificationCore,
     "classification_choice": ClassificationChoiceCore,
     "classification_choice_descriptor": ClassificationChoiceDescriptorCore,
     "examination": ExaminationCore,
     "finding": FindingCore,
     "finding_type": FindingTypeCore,
     "indication": IndicationCore,
     "indication_type": IndicationTypeCore,
     "intervention": InterventionCore,
     "intervention_type": InterventionTypeCore,
     "unit": UnitCore,
     "unit_type": UnitTypeCore,
     "information_source": InformationSourceCore,
     "information_source_type": InformationSourceTypeCore,
     "citation": CitationCore,
 }
 
 _LIST_FIELDS: dict[CoreConceptName, list[str]] = {
     "classification": ["classification_choices", "classification_types"],
     "classification_choice": ["classification_choice_descriptors"],
     "classification_choice_descriptor": ["selection_options"],
     "examination": ["findings", "examination_types", "indications"],
     "finding": ["finding_types", "classifications", "interventions"],
     "finding_type": [],
     "indication": ["indication_types", "classifications", "interventions"],
     "indication_type": [],
     "intervention": ["intervention_types"],
     "intervention_type": [],
     "unit": ["unit_types"],
     "unit_type": [],
     "information_source": ["information_source_types"],
     "information_source_type": [],
     "citation": ["keywords"],
 }
 
 _DICT_FIELDS: dict[CoreConceptName, list[str]] = {
     "classification": [],
     "classification_choice": [],
     "classification_choice_descriptor": [
         "numeric_distribution_params",
         "selection_default_options",
     ],
     "examination": [],
     "finding": [],
     "finding_type": [],
     "indication": [],
     "indication_type": [],
     "intervention": [],
     "intervention_type": [],
     "unit": [],
     "unit_type": [],
     "information_source": [],
     "information_source_type": [],
     "citation": ["identifiers"],
 }
 
 _SCALAR_EXTRA_FIELDS: dict[CoreConceptName, list[str]] = {
     "classification": [],
     "classification_choice": [],
     "classification_choice_descriptor": [
         "classification_choice_descriptor_type",
         "unit",
         "numeric_min",
         "numeric_max",
         "numeric_distribution",
         "text_max_length",
         "default_value_str",
         "default_value_num",
         "default_value_bool",
         "selection_multiple",
         "selection_multiple_n_min",
         "selection_multiple_n_max",
     ],
     "examination": [],
     "finding": [],
     "finding_type": [],
     "indication": [],
     "indication_type": [],
     "intervention": [],
     "intervention_type": [],
     "unit": ["abbreviation"],
     "unit_type": [],
     "information_source": [],
     "information_source_type": [],
     "citation": [
         "citation_key",
         "title",
         "abstract",
         "publication_year",
         "publication_month",
         "journal",
         "publisher",
         "volume",
         "issue",
         "pages",
         "doi",
         "url",
         "entry_type",
         "language",
     ],
 }
 
 _KB_FIELDS: dict[CoreConceptName, str] = {
     "classification": "classification",
     "classification_choice": "classification_choice",
     "classification_choice_descriptor": "classification_choice_descriptor",
     "examination": "examination",
     "finding": "finding",
     "finding_type": "finding_type",
     "indication": "indication",
     "indication_type": "indication_type",
     "intervention": "intervention",
     "intervention_type": "intervention_type",
     "unit": "unit",
     "unit_type": "unit_type",
     "information_source": "information_source",
     "information_source_type": "information_source_type",
     "citation": "citation",
 }
 
 
-def _read_value(record: CoreConceptStorageRecord, field: str) -> Any:
+def _read_value(record: CoreConceptStorageRecord, field: str) -> JsonValue | None:
     if isinstance(record, Mapping):
-        return record.get(field)
-    return getattr(record, field, None)
+        value = record.get(field)
+        return cast(JsonValue | None, value)
+    value = getattr(record, field, None)
+    return cast(JsonValue | None, value)
 
 
-def _base_payload(record: CoreConceptStorageRecord) -> dict[str, Any]:
-    payload: dict[str, Any] = {
+def _base_payload(record: CoreConceptStorageRecord) -> dict[str, JsonValue]:
+    tags_value = _read_value(record, "tags")
+    payload: dict[str, JsonValue] = {
         "id": _read_value(record, "id"),
         "name": _read_value(record, "name"),
         "name_de": _read_value(record, "name_de"),
         "name_en": _read_value(record, "name_en"),
         "description": _read_value(record, "description"),
         "uuid": _read_value(record, "uuid"),
         "kb_module_name": _read_value(record, "kb_module_name"),
-        "tags": parse_str_list(_read_value(record, "tags")),
+        "tags": _coerce_json_values(parse_str_list(
+            cast(str | Sequence[str] | None, tags_value)
+        )),
     }
     # Canonical payloads should skip absent DB ids rather than emit None.
     if payload["id"] is None:
         payload.pop("id")
     return payload
 
 
 def _record_list_fields(
     concept: CoreConceptName,
     record: CoreConceptStorageRecord,
 ) -> list[str]:
     if isinstance(record, Mapping):
         return _LIST_FIELDS[concept]
 
     list_field_provider = cast(SupportsKnowledgeBaseListFields, record.__class__)
     fields = list_field_provider.list_type_fields()
     if not fields:
         return _LIST_FIELDS[concept]
 
     return [field for field in _LIST_FIELDS[concept] if field in fields]
 
 
 def record_to_core_concept(
     concept: CoreConceptName,
     record: CoreConceptStorageRecord,
 ) -> CoreConceptModel:
     """Convert KB model instance or dict-like storage record into canonical shape."""
 
     model_cls = _CONCEPT_MODEL_LOOKUP[concept]
     payload = _base_payload(record)
 
     for field in _record_list_fields(concept, record):
-        payload[field] = parse_str_list(_read_value(record, field))
+        payload[field] = _coerce_json_values(
+            parse_str_list(cast(str | Sequence[str] | None, _read_value(record, field)))
+        )
 
     for field in _DICT_FIELDS[concept]:
         value = _read_value(record, field)
         payload[field] = dict(value) if isinstance(value, Mapping) else {}
 
     for field in _SCALAR_EXTRA_FIELDS[concept]:
         payload[field] = _read_value(record, field)
 
     return model_cls.model_validate(payload)
 
 
+def _coerce_json_values(values: list[str]) -> list[JsonValue]:
+    return [cast(JsonValue, value) for value in values]
+
+
 def core_concept_to_storage(
     concept: CoreConceptName,
-    value: CoreConceptModel | Mapping[str, Any],
-) -> dict[str, Any]:
+    value: CoreConceptModel | Mapping[str, JsonValue],
+) -> dict[str, JsonValue]:
     """Convert canonical concept payload back to storage-compatible representation."""
 
     model_cls = _CONCEPT_MODEL_LOOKUP[concept]
     model_value = model_cls.model_validate(value)
-    payload = model_value.model_dump(mode="python", exclude_none=True)
+    payload = cast(
+        dict[str, JsonValue],
+        model_value.model_dump(mode="python", exclude_none=True),
+    )
 
-    payload["tags"] = serialize_str_list(model_value.tags)
+    payload["tags"] = serialize_str_list(cast(list[str], model_value.tags))
 
     for field in _LIST_FIELDS[concept]:
-        payload[field] = serialize_str_list(payload.get(field, []))
+        list_value = payload.get(field, [])
+        if not isinstance(list_value, (list, tuple)):
+            list_value = None
+        payload[field] = serialize_str_list(cast(Sequence[str] | None, list_value))
 
     # KB/YAML storage is keyed by semantic names; API-facing numeric ids are optional.
     payload.pop("id", None)
 
     return payload
 
 
 def records_to_core_concepts(
     concept: CoreConceptName,
     records: Iterable[CoreConceptStorageRecord],
 ) -> list[CoreConceptModel]:
     return [record_to_core_concept(concept, record) for record in records]
 
 
-def kb_to_core_concepts_payload(kb: Any) -> CoreConceptCollection:
+def kb_to_core_concepts_payload(kb: _KnowledgeBaseLike) -> CoreConceptCollection:
     """Export all supported core KB concepts into canonical cross-layer payload."""
 
     module_name = getattr(getattr(kb, "config", None), "name", "unknown")
 
     payload = {
         "module_name": module_name,
     }
 
     for concept, kb_field in _KB_FIELDS.items():
         entries = getattr(kb, kb_field, {})
         values = entries.values() if isinstance(entries, Mapping) else []
-        payload[kb_field] = records_to_core_concepts(concept, values)
+        payload[kb_field] = records_to_core_concepts(
+            concept,
+            cast(Iterable[CoreConceptStorageRecord], values),
+        )
 
     return CoreConceptCollection.model_validate(payload)
 
 
 def canonical_payload_to_storage(
-    payload: CoreConceptCollection | Mapping[str, Any],
-) -> dict[str, Any]:
+    payload: CoreConceptCollection | Mapping[str, JsonValue],
+) -> dict[str, JsonValue]:
     """Convert canonical payload collections back to storage-compatible records."""
 
     collection = CoreConceptCollection.model_validate(payload)
-    out: dict[str, Any] = {"module_name": collection.module_name}
+    out: dict[str, JsonValue] = {"module_name": collection.module_name}
 
     for concept, kb_field in _KB_FIELDS.items():
         concept_values = getattr(collection, kb_field)
         out[kb_field] = [
             core_concept_to_storage(concept, value) for value in concept_values
         ]
 
     return out
 
 
 __all__ = [
     "CoreConceptName",
     "CoreConceptModel",
     "record_to_core_concept",
     "records_to_core_concepts",
     "core_concept_to_storage",
     "kb_to_core_concepts_payload",
     "canonical_payload_to_storage",
 ]
diff --git lx_dtypes/models/contracts/ai_dataset.py lx_dtypes/models/contracts/ai_dataset.py
index 33465a7..17ad647 100644
--- lx_dtypes/models/contracts/ai_dataset.py
+++ lx_dtypes/models/contracts/ai_dataset.py
@@ -1,475 +1,480 @@
 # endoreg_db/contracts/ai_dataset.py
 
 from __future__ import annotations
 
 from datetime import datetime
 from typing import Literal
 from uuid import UUID
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
 
 from lx_dtypes.models.contracts.json_types import JsonObject
 
 
 class AIDataSetScoredActiveLearningCandidateContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     sample_index: int
     video_id: int
     frame_number: int
     frame_id: int
     timestamp: float
     segment_id: int
     probs: list[float]
     quality_score: float
     uncertainty: float
     diversity: float
     rarity: float
     quality_gate: float
     frame_score: float
 
 
 DatasetType = Literal["image", "video"]
 AIModelType = Literal[
     "image_multilabel_classification",
     "video_segment_classification",
 ]
 TrainingRunStatus = Literal["queued", "running", "completed", "failed", "lost"]
 ExportArtifactStatus = Literal["running", "completed", "failed"]
 
 
 def _empty_selected_sample_indices() -> list[int]:
     return []
 
 
 def _empty_selected_frame_ids() -> list[int]:
     return []
 
 
 def _empty_selected_candidates() -> list[
     AIDataSetScoredActiveLearningCandidateContract
 ]:
     return []
 
 
 class AIDataSetActiveLearningConfigContractContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     budget: int = 32
     segment_gap_frames: int = 150
     temporal_spacing_frames: int = 75
     min_quality_score: float = 0.35
     max_samples_per_segment: int = 1
     max_rarity_boost: float = 2.0
     max_label_weight: float = 3.0
 
     @field_validator(
         "budget",
         "segment_gap_frames",
         "temporal_spacing_frames",
         "max_samples_per_segment",
     )
     @classmethod
     def positive_int(cls, value: int) -> int:
         if value <= 0:
             raise ValueError("value must be positive")
         return value
 
     @field_validator("min_quality_score")
     @classmethod
     def quality_score_range(cls, value: float) -> float:
         if not 0.0 <= value <= 1.0:
             raise ValueError("min_quality_score must be between 0 and 1")
         return value
 
     @field_validator("max_rarity_boost", "max_label_weight")
     @classmethod
     def positive_float(cls, value: float) -> float:
         if value <= 0.0:
             raise ValueError("value must be positive")
         return value
 
 
 class AIDataSetActiveLearningCandidateContractContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     sample_index: int
     video_id: int
     frame_number: int
     frame_id: int
     timestamp: float
     probs: list[float]
     embedding: list[float]
     quality_score: float
 
     @field_validator("probs", "embedding")
     @classmethod
     def non_empty_float_vector(cls, value: list[float]) -> list[float]:
         if not value:
             raise ValueError("vector must not be empty")
         return value
 
     @field_validator("quality_score")
     @classmethod
     def optional_quality_score_range(cls, value: float | None) -> float | None:
         if value is None:
             return None
         if not 0.0 <= value <= 1.0:
             raise ValueError("quality_score must be between 0 and 1")
         return value
 
 
 class AIDataSetScoredActiveLearningCandidateContractContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     sample_index: int
     video_id: int
     frame_number: int
     frame_id: int
     timestamp: float
     segment_id: int
     probs: list[float]
     quality_score: float
     uncertainty: float
     diversity: float
     rarity: float
     quality_gate: float
     frame_score: float
 
 
 class AIDataSetActiveLearningSelectionContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     config: AIDataSetActiveLearningConfigContractContract
     candidate_count: int
     segment_count: int
     selected_sample_indices: list[int] = Field(
         default_factory=_empty_selected_sample_indices,
     )
     selected_frame_ids: list[int] = Field(default_factory=_empty_selected_frame_ids)
     selected_candidates: list[AIDataSetScoredActiveLearningCandidateContract] = Field(
         default_factory=_empty_selected_candidates,
     )
 
 
 class AIDataSetContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     id: int
     name: str
     description: str
     ai_model_type: AIModelType = "image_multilabel_classification"
     dataset_type: DatasetType = "image"
     is_active: bool = True
     created_at: datetime
     updated_at: datetime
 
     image_annotation_count: int = 0
     video_annotation_count: int = 0
 
     @field_validator("name", "description", mode="before")
     @classmethod
-    def blank_string_to_none(cls, value: object) -> object:
+    def blank_string_to_none(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             stripped = value.strip()
             return stripped or ""
-        return value
+        return str(value)
 
     @model_validator(mode="after")
     def validate_model_type_matches_dataset_type(self) -> AIDataSetContract:
         expected_by_dataset_type: dict[str, str] = {
             "image": "image_multilabel_classification",
             "video": "video_segment_classification",
         }
         expected = expected_by_dataset_type[self.dataset_type]
         if self.ai_model_type != expected:
             raise ValueError(
                 f"ai_model_type={self.ai_model_type!r} is not compatible with "
                 f"dataset_type={self.dataset_type!r}; expected {expected!r}."
             )
         return self
 
 
 class AIDataSetCreateContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     name: str
     description: str = ""
     dataset_type: DatasetType = "image"
     ai_model_type: AIModelType
     is_active: bool = True
 
     @field_validator("name")
     @classmethod
     def validate_name(cls, value: str) -> str:
         normalized = value.strip()
         if not normalized:
             raise ValueError("name is required")
         if len(normalized) > 255:
             raise ValueError("name must be 255 characters or fewer")
         return normalized
 
     @field_validator("description")
     @classmethod
     def normalize_description(cls, value: str) -> str:
         return value.strip()
 
     @model_validator(mode="after")
     def fill_and_validate_ai_model_type(self) -> AIDataSetCreateContract:
         expected_by_dataset_type: dict[str, AIModelType] = {
             "image": "image_multilabel_classification",
             "video": "video_segment_classification",
         }
         expected = expected_by_dataset_type[self.dataset_type]
         if self.ai_model_type is None:
             self.ai_model_type = expected
         elif self.ai_model_type != expected:
             raise ValueError("ai_model_type is not compatible with dataset_type")
         return self
 
 
 class AIDataSetAttachVideoContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     video_id: int
     frame_annotation_ids: list[int] = Field(default_factory=list)
     segment_ids: list[int] = Field(default_factory=list)
     include_all_annotations: bool = False
     include_frame_annotations: bool = True
     include_video_annotations: bool = True
     information_source_names: list[str] = Field(default_factory=list)
 
     @field_validator("information_source_names", mode="before")
     @classmethod
-    def normalize_information_source_names(cls, value: object) -> object:
+    def normalize_information_source_names(
+        cls, value: list[str] | str | None
+    ) -> list[str] | None:
         if value in (None, ""):
             return []
         if isinstance(value, str):
             return [item.strip() for item in value.split(",") if item.strip()]
         return value
 
     @field_validator("information_source_names")
     @classmethod
     def strip_information_source_names(cls, value: list[str]) -> list[str]:
         return [item.strip() for item in value if item.strip()]
 
 
 class AIDataSetAttachmentResultContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     dataset_id: int
     video_id: int
     frame_annotation_count: int = 0
     video_annotation_count: int = 0
     attached_frame_annotation_count: int = 0
     attached_segment_count: int = 0
     attached_frame_annotation_ids: list[int] = Field(default_factory=list)
     attached_segment_ids: list[int] = Field(default_factory=list)
 
 
 class AIModelTrainingRunContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     id: int
     run_id: UUID
     run_key: str
     dataset_id: int
     dataset_name: str
     dataset_type: str = ""
     ai_model_type: str = ""
 
     backbone_name: str
     feature_mode: str
     freeze_backbone: bool
     epochs: int
     batch_size: int
     labelset_version: int
     treat_unlabeled_as_negative: bool
     backbone_checkpoint: str
 
     request_payload: JsonObject = Field(default_factory=dict)
     command_kwargs: JsonObject = Field(default_factory=dict)
 
     status: TrainingRunStatus
     server_instance_id: str = ""
     result: JsonObject
     artifact_paths: dict[str, str] = Field(default_factory=dict)
     error: str = ""
     stdout: str = ""
     stderr: str = ""
 
     created_at: datetime
     updated_at: datetime
     started_at: datetime
     finished_at: datetime
 
     is_terminal: bool = False
 
 
 class AIModelTrainingRunCreateContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     dataset_id: int
     training_target: str = "image_multilabel"
 
     backbone_name: str = "resnet50_imagenet"
     feature_mode: str = "freeze_backbone"
     epochs: int = 10
     batch_size: int = 32
     labelset_version: int = 1
     device: str = "auto"
     annotation_source_scope: str = "all"
     treat_unlabeled_as_negative: bool = True
     backbone_checkpoint: str
 
     @field_validator("epochs", "batch_size", "labelset_version")
     @classmethod
     def validate_positive_int(cls, value: int) -> int:
         if value <= 0:
             raise ValueError("value must be positive")
         return value
 
 
 class AIModelTrainingRunUpdateContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     status: TrainingRunStatus | None = None
     result: JsonObject | None = None
     artifact_paths: dict[str, str] | None = None
     error: str | None = None
     stdout: str | None = None
     stderr: str | None = None
     started_at: datetime | None = None
     finished_at: datetime | None = None
 
 
 class AIDataSetExportArtifactContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     id: int
     artifact_id: UUID
     artifact_key: str
     dataset_id: int
     dataset_name: str
     dataset_type: str = ""
     ai_model_type: str = ""
 
     request_payload: JsonObject = Field(default_factory=dict)
     center_key: str
     all_centers: bool = False
     only_validated: bool = True
 
     status: ExportArtifactStatus
     output_path: str = ""
     download_filename: str = ""
     sha256: str = ""
     byte_size: int = 0
     summary: JsonObject = Field(default_factory=dict)
     error: str = ""
 
     created_at: datetime
     updated_at: datetime
     finished_at: datetime
 
     @field_validator("sha256")
     @classmethod
     def validate_sha256_or_blank(cls, value: str) -> str:
         if value and len(value) != 64:
             raise ValueError("sha256 must be blank or exactly 64 characters")
         return value
 
 
 class AIDataSetExportCreateContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     dataset_id: int
     ai_dataset_name: str
     ai_dataset_type: DatasetType
     center_key: str
     all_centers: bool = False
     only_validated: bool = True
 
     @field_validator("center_key", "ai_dataset_name", mode="before")
     @classmethod
-    def blank_to_none(cls, value: object) -> object:
+    def blank_to_none(cls, value: str | int | float | bool | None) -> str | None:
         if value is None:
             return None
         if isinstance(value, str):
             return value.strip() or None
-        return value
+        converted = str(value).strip()
+        return converted or None
 
     @model_validator(mode="after")
     def validate_dataset_selector(self) -> AIDataSetExportCreateContract:
         has_id = self.dataset_id
         has_name_type = bool(self.ai_dataset_name and self.ai_dataset_type)
         if not has_id and not has_name_type:
             raise ValueError(
                 "Provide dataset_id or both ai_dataset_name and ai_dataset_type."
             )
         return self
 
     @model_validator(mode="after")
     def validate_center_scope(self) -> AIDataSetExportCreateContract:
         if self.center_key and self.all_centers:
             raise ValueError("Use center_key or all_centers, not both.")
         return self
 
 
 class AITrainingManifestBuildContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     label_set_id: int
     treat_unlabeled_as_negative: bool = False
     include_file_paths: bool = False
     check_frame_format: bool = True
     preprocessing_strategy: Literal[
         "preserve_dimensions_black_mask",
         "crop_to_endoscope_roi",
     ] = "preserve_dimensions_black_mask"
     recommended_model_input_strategy: Literal[
         "preserve_dimensions_black_mask",
         "crop_to_endoscope_roi",
     ] = "crop_to_endoscope_roi"
     information_source_names: list[str]
 
     @field_validator("information_source_names", mode="before")
     @classmethod
-    def normalize_information_source_names(cls, value: object) -> object:
+    def normalize_information_source_names(
+        cls, value: list[str] | str | None
+    ) -> list[str] | None:
         if value in (None, ""):
             return None
         if isinstance(value, str):
             names = [item.strip() for item in value.split(",") if item.strip()]
             return names or None
         return value
 
 
 class AIDataSetStandardExportScopeContract(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     center_key: str
     all_centers: bool = False
     only_validated: bool = False
 
     @field_validator("center_key", mode="before")
     @classmethod
-    def normalize_center_key(cls, value: object) -> object:
+    def normalize_center_key(cls, value: str | int | float | bool | None) -> str | None:
         if value is None:
             return None
         if isinstance(value, str):
             return value.strip() or None
-        return value
+        return str(value).strip() or None
 
     @model_validator(mode="after")
     def validate_scope(self) -> AIDataSetStandardExportScopeContract:
         if self.center_key and self.all_centers:
             raise ValueError("Use center_key or all_centers, not both.")
         return self
 
 
 # Backward-compatible public aliases expected by endoreg_db.
 AIDataSetActiveLearningConfigContract = AIDataSetActiveLearningConfigContractContract
 AIDataSetActiveLearningCandidateContract = (
     AIDataSetActiveLearningCandidateContractContract
 )
diff --git lx_dtypes/models/contracts/ai_model.py lx_dtypes/models/contracts/ai_model.py
index 2b8a973..dbd9649 100644
--- lx_dtypes/models/contracts/ai_model.py
+++ lx_dtypes/models/contracts/ai_model.py
@@ -1,39 +1,42 @@
 from __future__ import annotations
 
+from collections.abc import Mapping
 from pydantic import BaseModel, ConfigDict, Field
 
+from .json_types import JsonObject
+
 
 class AiModelSerializerInputPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True)
 
     name: str = Field(min_length=1)
     description: str | None = None
     model_type: str = Field(min_length=1)
 
 
 class AiModelSerializerOutputPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", strict=True)
 
     name: str
     description: str | None = None
     model_type: str
 
 
 def validate_ai_model_serializer_input_payload(
-    payload: object,
+    payload: JsonObject,
 ) -> AiModelSerializerInputPayload:
     return AiModelSerializerInputPayload.model_validate(payload)
 
 
 def validate_ai_model_serializer_output_payload(
-    payload: object,
+    payload: JsonObject,
 ) -> AiModelSerializerOutputPayload:
     return AiModelSerializerOutputPayload.model_validate(payload)
 
 
 __all__ = [
     "AiModelSerializerInputPayload",
     "AiModelSerializerOutputPayload",
     "validate_ai_model_serializer_input_payload",
     "validate_ai_model_serializer_output_payload",
 ]
diff --git lx_dtypes/models/contracts/ai_prediction.py lx_dtypes/models/contracts/ai_prediction.py
index eba5391..7ee4d35 100644
--- lx_dtypes/models/contracts/ai_prediction.py
+++ lx_dtypes/models/contracts/ai_prediction.py
@@ -1,136 +1,175 @@
 from __future__ import annotations
 
 from pathlib import Path
-from collections.abc import Callable, Mapping
+from collections.abc import Callable, Mapping, Sequence
 from typing import TypeAlias, cast
 
 from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator
 
 JsonPath: TypeAlias = str
-ActivationCallable: TypeAlias = Callable[[object], object]
+ActivationCallable: TypeAlias = Callable[[float], float]
+
+
+def _coerce_float(value: object) -> float:
+    if isinstance(value, bool):
+        return float(int(value))
+    if isinstance(value, (int, float)):
+        return float(value)
+    if isinstance(value, str):
+        stripped = value.strip()
+        if not stripped:
+            raise ValueError("value cannot be blank")
+        return float(stripped)
+    raise ValueError("value must be numeric")
+
+
+def _coerce_int(value: object) -> int:
+    if isinstance(value, bool):
+        return int(value)
+    if isinstance(value, (int,)):
+        return int(value)
+    if isinstance(value, float):
+        if not value.is_integer():
+            raise ValueError("value must be an integer")
+        return int(value)
+    if isinstance(value, str):
+        stripped = value.strip()
+        if not stripped:
+            raise ValueError("value cannot be blank")
+        return int(float(stripped)) if "." in stripped else int(stripped)
+    raise ValueError("value must be an integer")
 
 
 class AiPredictionConfigPayload(BaseModel):
     model_config = ConfigDict(
         extra="forbid",
         frozen=True,
         strict=True,
         arbitrary_types_allowed=True,
     )
 
     mean: tuple[float, float, float]
     std: tuple[float, float, float]
     size_x: int = Field(ge=1)
     size_y: int = Field(ge=1)
     axes: list[int] = Field(
         default_factory=lambda: [2, 0, 1], min_length=3, max_length=3
     )
     batchsize: int = Field(ge=1)
     num_workers: int = Field(ge=0)
     activation: ActivationCallable | None = None
     labels: list[str]
-
     @field_validator("mean", "std", mode="before")
     @classmethod
     def _normalize_float_triplet(
         cls,
-        value: object,
+        value: Sequence[object],
     ) -> tuple[float, float, float]:
         if isinstance(value, (list, tuple)) and len(value) == 3:
-            return (float(value[0]), float(value[1]), float(value[2]))
+            return (
+                _coerce_float(value[0]),
+                _coerce_float(value[1]),
+                _coerce_float(value[2]),
+            )
         raise ValueError("mean/std must contain exactly three numeric values")
 
     @field_validator("axes", mode="before")
     @classmethod
-    def _normalize_axes(cls, value: object) -> list[int]:
+    def _normalize_axes(cls, value: Sequence[object]) -> list[int]:
         if isinstance(value, (list, tuple)) and len(value) == 3:
-            return [int(value[0]), int(value[1]), int(value[2])]
+            return [
+                _coerce_int(value[0]),
+                _coerce_int(value[1]),
+                _coerce_int(value[2]),
+            ]
         raise ValueError("axes must contain exactly three integer values")
 
 
 class AiPredictionResultPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     labels: list[str]
     paths: list[JsonPath]
     predictions: list[list[float]]
 
 
 class AiPredictionSequencePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     start: int
     stop: int
 
 
 class AiPredictionPostProcessPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     predictions: dict[str, list[float]]
     smooth_predictions: dict[str, list[float]]
     binary_predictions: dict[str, list[bool]]
     raw_sequences: dict[str, list[AiPredictionSequencePayload]]
     filtered_sequences: dict[str, list[AiPredictionSequencePayload]]
 
 
 class AiPredictionSerializablePostProcessPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     predictions: dict[str, list[float]]
     smooth_predictions: dict[str, list[float]]
     binary_predictions: dict[str, list[bool]]
     raw_sequences: dict[str, dict[str, list[int]]]
     filtered_sequences: dict[str, dict[str, list[int]]]
 
 
 class VideoSegmentsPayload(RootModel[dict[str, list[tuple[int, int]]]]):
     model_config = ConfigDict(frozen=True, strict=True)
 
     @field_validator("root", mode="before")
     @classmethod
-    def _normalize_root(cls, value: object) -> dict[str, list[tuple[int, int]]]:
+    def _normalize_root(
+        cls, value: Mapping[str, list[tuple[object, object]]] | Mapping[object, object]
+    ) -> dict[str, list[tuple[int, int]]]:
         if not isinstance(value, Mapping):
             raise ValueError("Video sequences payload must be a JSON object.")
 
         normalized: dict[str, list[tuple[int, int]]] = {}
         for label, raw_sequences in cast(Mapping[object, object], value).items():
             if not isinstance(label, str) or raw_sequences is None:
                 continue
             if not isinstance(raw_sequences, list):
                 raise ValueError(
                     f"Sequences for label '{label}' must be a list of pairs."
                 )
             converted: list[tuple[int, int]] = []
             for item in raw_sequences:
                 if not isinstance(item, (list, tuple)) or len(item) != 2:
                     raise ValueError(
                         f"Invalid sequence entry for label '{label}': {item!r}"
                     )
                 if not isinstance(item[0], int) or not isinstance(item[1], int):
                     raise ValueError(
                         f"Sequence coordinates must be int for label '{label}': {item!r}"
                     )
                 converted.append((int(item[0]), int(item[1])))
             normalized[label] = converted
 
         return normalized
 
     def as_dict(self) -> dict[str, list[tuple[int, int]]]:
         return cast(dict[str, list[tuple[int, int]]], self.model_dump(mode="python"))
 
 
 def to_json_path(path: str | Path) -> JsonPath:
     return str(path)
 
 
 __all__ = [
     "ActivationCallable",
     "AiPredictionConfigPayload",
     "AiPredictionPostProcessPayload",
     "AiPredictionResultPayload",
     "VideoSegmentsPayload",
     "AiPredictionSequencePayload",
     "AiPredictionSerializablePostProcessPayload",
     "JsonPath",
     "to_json_path",
 ]
diff --git lx_dtypes/models/contracts/anonymization.py lx_dtypes/models/contracts/anonymization.py
index 224c14f..6647ff5 100644
--- lx_dtypes/models/contracts/anonymization.py
+++ lx_dtypes/models/contracts/anonymization.py
@@ -1,105 +1,105 @@
 from __future__ import annotations
 
 from datetime import datetime
 from typing import Literal, TypedDict
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 AnonymizationMediaType = Literal["video", "pdf"]
 AnonymizationStartResult = Literal["video", "pdf"]
 AnonymizationValidationResult = Literal["video", "pdf"]
 
 
 class AnonymizationStatusData(TypedDict):
     media_type: AnonymizationMediaType
     anonymization_status: str
     file_exists: bool
     integrity_status: str | None
     integrity_error: str | None
     uuid: str | None
     hash: str | None
 
 
 class AnonymizationListItemData(TypedDict):
     id: int
     media_type: AnonymizationMediaType
     anonymization_status: str
     created_at: datetime | None
     updated_at: datetime | None
 
 
 class AnonymizationStatusPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, str_strip_whitespace=True)
 
     media_type: AnonymizationMediaType
     anonymization_status: str
     file_exists: bool
     integrity_status: str | None = None
     integrity_error: str | None = None
     uuid: str | None = None
     hash: str | None = None
 
     @field_validator(
         "anonymization_status",
         "integrity_status",
         "integrity_error",
         "uuid",
         "hash",
         mode="before",
     )
     @classmethod
-    def blank_to_none(cls, value: object) -> object:
+    def blank_to_none(cls, value: str | int | float | bool | None) -> str | None:
         if value is None:
             return None
         if isinstance(value, str):
             return value.strip() or None
         return str(value).strip() or None
 
 
 class AnonymizationListItemPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, str_strip_whitespace=True)
 
     id: int = Field(ge=1)
     media_type: AnonymizationMediaType
     anonymization_status: str
     created_at: datetime | None = None
     updated_at: datetime | None = None
 
 
 def dump_anonymization_status_payload(
     payload: AnonymizationStatusPayload,
 ) -> AnonymizationStatusData:
     return AnonymizationStatusData(
         media_type=payload.media_type,
         anonymization_status=payload.anonymization_status,
         file_exists=payload.file_exists,
         integrity_status=payload.integrity_status,
         integrity_error=payload.integrity_error,
         uuid=payload.uuid,
         hash=payload.hash,
     )
 
 
 def dump_anonymization_list_item_payload(
     payload: AnonymizationListItemPayload,
 ) -> AnonymizationListItemData:
     return AnonymizationListItemData(
         id=payload.id,
         media_type=payload.media_type,
         anonymization_status=payload.anonymization_status,
         created_at=payload.created_at,
         updated_at=payload.updated_at,
     )
 
 
 __all__ = [
     "AnonymizationListItemData",
     "AnonymizationListItemPayload",
     "AnonymizationMediaType",
     "AnonymizationStartResult",
     "AnonymizationStatusData",
     "AnonymizationStatusPayload",
     "AnonymizationValidationResult",
     "dump_anonymization_list_item_payload",
     "dump_anonymization_status_payload",
 ]
diff --git lx_dtypes/models/contracts/assessment.py lx_dtypes/models/contracts/assessment.py
index 8d65bc2..7dfd42e 100644
--- lx_dtypes/models/contracts/assessment.py
+++ lx_dtypes/models/contracts/assessment.py
@@ -1,22 +1,22 @@
 from __future__ import annotations
 
 from pydantic import BaseModel, ConfigDict, field_validator
 
 
 class AssessmentRecord(BaseModel):
     model_config = ConfigDict(extra="ignore", strict=True)
 
     file: str = ""
     report_id: str = ""
     first_name: str = ""
     last_name: str = ""
 
     @field_validator("file", "report_id", "first_name", "last_name", mode="before")
     @classmethod
-    def normalize_text_fields(cls, value: object) -> str:
+    def normalize_text_fields(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         return str(value).strip()
 
 
 __all__ = ["AssessmentRecord"]
diff --git lx_dtypes/models/contracts/case_resolution.py lx_dtypes/models/contracts/case_resolution.py
index 77464ca..955f55c 100644
--- lx_dtypes/models/contracts/case_resolution.py
+++ lx_dtypes/models/contracts/case_resolution.py
@@ -1,119 +1,119 @@
 from __future__ import annotations
 
 from datetime import date
-from typing import Any, Literal
+from typing import Literal
 
 from pydantic import (
     BaseModel,
     ConfigDict,
     ValidationError,
     field_validator,
     model_validator,
 )
 
 
 class CaseResolutionNewPatient(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     first_name: str
     last_name: str
     dob: date | None = None
     gender: str | None = None
     center: str | None = None
     email: str | None = None
     phone: str | None = None
     is_real_person: bool = True
 
     @field_validator("first_name", "last_name", mode="before")
     @classmethod
-    def normalize_required_name(cls, value: Any) -> str:
+    def normalize_required_name(cls, value: str | int | float | None) -> str:
         normalized = str(value).strip()
         if not normalized:
             raise ValueError("value must not be empty")
         return normalized
 
     @field_validator("gender", "center", "email", "phone", mode="before")
     @classmethod
-    def normalize_optional_str(cls, value: Any) -> str | None:
+    def normalize_optional_str(cls, value: str | int | float | None) -> str | None:
         if value in (None, ""):
             return None
         normalized = str(value).strip()
         return normalized or None
 
 
 class CaseResolutionRequest(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     action: Literal["attach", "create", "defer"]
     patient_examination_id: int | None = None
     patient_id: int | None = None
     new_patient: CaseResolutionNewPatient | None = None
     examination_name: str | None = None
     date_start: date | None = None
     date_end: date | None = None
 
     @field_validator("patient_examination_id", "patient_id", mode="before")
     @classmethod
-    def coerce_optional_positive_int(cls, value: Any) -> int | None:
+    def coerce_optional_positive_int(cls, value: int | str | None) -> int | None:
         if value in (None, ""):
             return None
         normalized = int(str(value))
         if normalized <= 0:
             raise ValueError("value must be a positive integer")
         return normalized
 
     @field_validator("examination_name", mode="before")
     @classmethod
-    def normalize_examination_name(cls, value: Any) -> str | None:
+    def normalize_examination_name(cls, value: str | int | float | None) -> str | None:
         if value in (None, ""):
             return None
         normalized = str(value).strip()
         return normalized or None
 
     @model_validator(mode="after")
     def validate_action_requirements(self) -> "CaseResolutionRequest":
         if self.action == "attach":
             if self.patient_examination_id is None:
                 raise ValueError("patient_examination_id is required for attach action")
         elif self.action == "create":
             if (self.patient_id is None) == (self.new_patient is None):
                 raise ValueError(
                     "exactly one of patient_id or new_patient is required for create action"
                 )
             if self.examination_name is None:
                 raise ValueError("examination_name is required for create action")
             if self.date_end and self.date_start and self.date_end < self.date_start:
                 raise ValueError("date_end must be on or after date_start")
         elif any(
             value is not None
             for value in (
                 self.patient_examination_id,
                 self.patient_id,
                 self.new_patient,
                 self.examination_name,
                 self.date_start,
                 self.date_end,
             )
         ):
             raise ValueError("defer action must not include linkage fields")
         return self
 
 
 class CaseResolutionResponse(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     media_type: Literal["video", "pdf"]
     media_id: int
     action: Literal["attach", "create", "defer"]
     status: Literal["linked", "deferred"]
     patient_examination_id: int | None = None
     patient_id: int | None = None
     created: bool = False
 
 
 __all__ = [
     "CaseResolutionNewPatient",
     "CaseResolutionRequest",
     "CaseResolutionResponse",
     "ValidationError",
 ]
diff --git lx_dtypes/models/contracts/dicom_export.py lx_dtypes/models/contracts/dicom_export.py
index e2999e8..297e78a 100644
--- lx_dtypes/models/contracts/dicom_export.py
+++ lx_dtypes/models/contracts/dicom_export.py
@@ -1,152 +1,153 @@
 from __future__ import annotations
 
 import re
 from datetime import date, datetime
 from typing import Literal, Self
 from uuid import UUID
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
+from .json_types import JsonObject
 
 
 _DICOM_UID_PATTERN = re.compile(r"^[0-9]+(?:\.[0-9]+)+$")
 _SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$")
 
 
 class DicomContractModel(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
 
 class DicomValidationResult(DicomContractModel):
     validator_name: str = Field(min_length=1, max_length=128)
     validator_version: str = Field(min_length=1, max_length=64)
     status: Literal["passed"]
 
 
 class DicomDeidentification(DicomContractModel):
     profile: str = Field(min_length=1, max_length=255)
     method: str = Field(min_length=1, max_length=255)
     patient_identity_removed: Literal[True]
     clean_pixel_data: bool
 
 
 class DicomInstanceManifest(DicomContractModel):
     sop_instance_uid: str
     sop_class_uid: str
     transfer_syntax_uid: str
     instance_number: int | None = Field(default=None, ge=1)
     artifact_reference: str = Field(min_length=1, max_length=1024)
     artifact_class: Literal["anonymized_processed"]
     artifact_sha256: str
     size_bytes: int = Field(ge=1)
     masked_regions: int = Field(default=0, ge=0)
 
     @field_validator("sop_instance_uid", "sop_class_uid", "transfer_syntax_uid")
     @classmethod
     def validate_uid(cls, value: str) -> str:
         if len(value) > 64 or _DICOM_UID_PATTERN.fullmatch(value) is None:
             raise ValueError("DICOM UIDs must be numeric dotted values up to 64 chars")
         return value
 
     @field_validator("artifact_sha256")
     @classmethod
     def validate_sha256(cls, value: str) -> str:
         normalized = value.lower()
         if _SHA256_PATTERN.fullmatch(normalized) is None:
             raise ValueError("artifact_sha256 must be a SHA-256 hex digest")
         return normalized
 
     @field_validator("artifact_reference")
     @classmethod
     def validate_artifact_reference(cls, value: str) -> str:
         if value.startswith(("/", "\\")) or ".." in value.replace("\\", "/").split("/"):
             raise ValueError(
                 "artifact_reference must be a relative protected-storage key"
             )
         return value
 
 
 class DicomSeriesManifest(DicomContractModel):
     series_instance_uid: str
     modality: str = Field(min_length=1, max_length=16)
     series_number: int | None = Field(default=None, ge=1)
     instances: list[DicomInstanceManifest] = Field(min_length=1)
 
     @field_validator("series_instance_uid")
     @classmethod
     def validate_uid(cls, value: str) -> str:
         if len(value) > 64 or _DICOM_UID_PATTERN.fullmatch(value) is None:
             raise ValueError("DICOM UIDs must be numeric dotted values up to 64 chars")
         return value
 
     @field_validator("modality")
     @classmethod
     def normalize_modality(cls, value: str) -> str:
         return value.upper()
 
     @model_validator(mode="after")
     def validate_instance_uids(self) -> Self:
         uids = [item.sop_instance_uid for item in self.instances]
         if len(uids) != len(set(uids)):
             raise ValueError("SOP Instance UIDs must be unique within a series")
         return self
 
 
 class DicomStudyManifest(DicomContractModel):
     study_instance_uid: str
     patient_pseudonym: str = Field(min_length=1, max_length=255)
     accession_identifier: str | None = Field(default=None, max_length=255)
     study_date: date | None = None
     series: list[DicomSeriesManifest] = Field(min_length=1)
 
     @field_validator("study_instance_uid")
     @classmethod
     def validate_uid(cls, value: str) -> str:
         if len(value) > 64 or _DICOM_UID_PATTERN.fullmatch(value) is None:
             raise ValueError("DICOM UIDs must be numeric dotted values up to 64 chars")
         return value
 
     @model_validator(mode="after")
     def validate_series_uids(self) -> Self:
         series_uids = [item.series_instance_uid for item in self.series]
         if len(series_uids) != len(set(series_uids)):
             raise ValueError("Series Instance UIDs must be unique within a study")
         sop_uids = [
             instance.sop_instance_uid
             for series in self.series
             for instance in series.instances
         ]
         if len(sop_uids) != len(set(sop_uids)):
             raise ValueError("SOP Instance UIDs must be unique within an export")
         return self
 
 
 class DicomExportManifestV2(DicomContractModel):
     schema_version: Literal[2]
     export_id: UUID
     created_at: datetime
     source_system: str = Field(min_length=1, max_length=128)
     deidentification: DicomDeidentification
     validation: DicomValidationResult
     study: DicomStudyManifest
 
     @field_validator("created_at")
     @classmethod
     def require_aware_datetime(cls, value: datetime) -> datetime:
         if value.tzinfo is None or value.utcoffset() is None:
             raise ValueError("created_at must be timezone-aware")
         return value
 
 
-def validate_dicom_export_manifest_v2(value: object) -> DicomExportManifestV2:
+def validate_dicom_export_manifest_v2(value: JsonObject) -> DicomExportManifestV2:
     return DicomExportManifestV2.model_validate(value)
 
 
 __all__ = [
     "DicomDeidentification",
     "DicomExportManifestV2",
     "DicomInstanceManifest",
     "DicomSeriesManifest",
     "DicomStudyManifest",
     "DicomValidationResult",
     "validate_dicom_export_manifest_v2",
 ]
diff --git lx_dtypes/models/contracts/endoscopy_processor.py lx_dtypes/models/contracts/endoscopy_processor.py
index ec4d708..8910371 100644
--- lx_dtypes/models/contracts/endoscopy_processor.py
+++ lx_dtypes/models/contracts/endoscopy_processor.py
@@ -1,123 +1,129 @@
 from __future__ import annotations
 
+from collections.abc import Mapping
 from pathlib import Path
 
 from pydantic import BaseModel, ConfigDict
 from lx_dtypes.models.meta.VideoMeta import VideoRoiBox
+from lx_dtypes.models.contracts.json_types import JsonValue
 
 
 class RoiBoxCore(VideoRoiBox):
     """
     Rectangular region of Interest in Image or Video.
     Validates positive int values and four corners specification.
     """
 
 
 class EndoscopeImageRoiCore(RoiBoxCore):
     image_width: int
     image_height: int
 
 
 class EndoscopyProcessorCore(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str
 
 
 class MaskCallPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     input_video: Path
     mask_config: RoiBoxCore
     output_video: Path
     mode: str
 
 
 def roi_box_to_crop_template(
     roi: RoiBoxCore,
     *,
     image_width: int | None = None,
     image_height: int | None = None,
 ) -> list[int] | None:
     """
     Convert an ROI box into the crop-template shape used by video services.
 
     Returns [y1, y2, x1, x2]. If image dimensions are provided, the crop is
     clamped to the image boundary. Returns None if the ROI is empty or clamps
     to an empty region.
     """
     x = int(roi.x)
     y = int(roi.y)
     width = int(roi.width)
     height = int(roi.height)
 
     if width <= 0 or height <= 0:
         return None
 
     if image_width is not None and image_height is not None:
         y1 = max(0, y)
         y2 = min(int(image_height), y + height)
         x1 = max(0, x)
         x2 = min(int(image_width), x + width)
         if y1 >= y2 or x1 >= x2:
             return None
         return [y1, y2, x1, x2]
 
     return [y, y + height, x, x + width]
 
 
-def roi_box_from_object(value: object) -> RoiBoxCore:
+def roi_box_from_object(value: RoiBoxCore | Mapping[str, JsonValue]) -> RoiBoxCore:
     """
     Normalize a VideoMeta/processor ROI-like object into RoiBoxCore.
 
     Accepts an existing RoiBoxCore, a mapping, or an object with x/y/width/height
     attributes.
     """
     if isinstance(value, RoiBoxCore):
         return value
     return RoiBoxCore.model_validate(value)
 
 
-def roi_box_or_none_from_object(value: object | None) -> RoiBoxCore | None:
+def roi_box_or_none_from_object(
+    value: RoiBoxCore | Mapping[str, JsonValue] | None
+) -> (
+    RoiBoxCore | None
+):
     """Normalize a nullable ROI-like object into RoiBoxCore."""
     if value is None:
         return None
     return roi_box_from_object(value)
 
 
 def roi_box_to_legacy_dict(roi: RoiBoxCore) -> dict[str, int]:
     """
     Convert RoiBoxCore into the legacy dict shape still used by some helpers.
 
     This is intentionally centralized here so service code does not hand-roll
     dicts or index Pydantic models as mappings.
     """
     return {
         "x": int(roi.x),
         "y": int(roi.y),
         "width": int(roi.width),
         "height": int(roi.height),
     }
 
 
 def all_black_fallback_roi_box() -> RoiBoxCore:
     """
     Return a valid ROI for code paths where all_black=True makes ROI irrelevant.
 
     RoiBoxCore validates positive dimensions, so use a minimal valid box instead
     of {} or zero-sized dicts.
     """
     return RoiBoxCore(x=0, y=0, width=1, height=1)
 
 
 __all__ = [
     "RoiBoxCore",
     "EndoscopeImageRoiCore",
     "EndoscopyProcessorCore",
     "MaskCallPayload",
     "roi_box_to_legacy_dict",
     "roi_box_or_none_from_object",
     "all_black_fallback_roi_box",
     "roi_box_from_object",
     "roi_box_to_crop_template",
 ]
diff --git lx_dtypes/models/contracts/event.py lx_dtypes/models/contracts/event.py
index 4e1be57..d272b17 100644
--- lx_dtypes/models/contracts/event.py
+++ lx_dtypes/models/contracts/event.py
@@ -1,51 +1,52 @@
 from __future__ import annotations
 from pydantic import BaseModel, ConfigDict, Field
+from lx_dtypes.models.contracts.json_types import JsonValue
 
 # Common configuration shared across your core DTOs
 # Keeps data immutable, strict, and prevents unexpected fields
 core_config = ConfigDict(extra="forbid", frozen=True, strict=True, from_attributes=True)
 
 
 class EventCore(BaseModel):
     model_config = core_config
 
     name: str
     description: str | None = None
 
 
 class EventClassificationCore(BaseModel):
     model_config = core_config
 
     name: str
     event_name: str  # Matches your Django natural key approach
 
 
 class EventClassificationChoiceCore(BaseModel):
     model_config = core_config
 
     name: str
     event_classification_name: str
     # Typed dicts slightly better by specifying keys are strings
-    subcategories: dict[str, object] = Field(default_factory=dict)
-    numerical_descriptors: dict[str, object] = Field(default_factory=dict)
+    subcategories: dict[str, JsonValue] = Field(default_factory=dict)
+    numerical_descriptors: dict[str, JsonValue] = Field(default_factory=dict)
 
 
 class EventClassificationDeep(EventClassificationCore):
     """Includes all choices nested under the classification."""
 
     choices: list[EventClassificationChoiceCore] = Field(default_factory=list)
 
 
 class EventDeep(EventCore):
     """Includes all classifications nested under the event."""
 
     classifications: list[EventClassificationDeep] = Field(default_factory=list)
 
 
 __all__ = [
     "EventCore",
     "EventClassificationCore",
     "EventClassificationChoiceCore",
     "EventClassificationDeep",
     "EventDeep",
 ]
diff --git lx_dtypes/models/contracts/examination_indication.py lx_dtypes/models/contracts/examination_indication.py
index b3c8a4b..e5e1f1c 100644
--- lx_dtypes/models/contracts/examination_indication.py
+++ lx_dtypes/models/contracts/examination_indication.py
@@ -1,32 +1,33 @@
 from __future__ import annotations
 
 from pydantic import BaseModel, ConfigDict, Field
+from lx_dtypes.models.contracts.json_types import JsonValue
 
 
 class ExaminationIndicationCore(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str
     description: str | None = None
 
 
 class ExaminationIndicationClassificationCore(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str
     description: str | None = None
 
 
 class ExaminationIndicationClassificationChoiceCore(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str
-    subcategories: dict[str, object] = Field(default_factory=dict)
-    numerical_descriptors: dict[str, object] = Field(default_factory=dict)
+    subcategories: dict[str, JsonValue] = Field(default_factory=dict)
+    numerical_descriptors: dict[str, JsonValue] = Field(default_factory=dict)
 
 
 __all__ = [
     "ExaminationIndicationCore",
     "ExaminationIndicationClassificationCore",
     "ExaminationIndicationClassificationChoiceCore",
 ]
diff --git lx_dtypes/models/contracts/export_annotated.py lx_dtypes/models/contracts/export_annotated.py
index aa70480..cf31b81 100644
--- lx_dtypes/models/contracts/export_annotated.py
+++ lx_dtypes/models/contracts/export_annotated.py
@@ -1,196 +1,238 @@
 # endoreg_db/contracts/export_annotated.py
 
 from __future__ import annotations
 
+from collections.abc import Mapping
 from pathlib import Path
-from typing import Any, Literal, cast
+from typing import Literal, Sequence, cast
 
 import yaml
 from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
 
 from lx_dtypes.models.contracts.video_frame_export import export_config
+from lx_dtypes.models.contracts.json_types import JsonValue
+
+
+def _coerce_int(value: object) -> int:
+    if isinstance(value, bool):
+        return int(value)
+    if isinstance(value, int):
+        return int(value)
+    if isinstance(value, float):
+        if not value.is_integer():
+            raise ValueError("segment ids must be integer values")
+        return int(value)
+    if isinstance(value, str):
+        stripped = value.strip()
+        if not stripped:
+            return 0
+        return int(stripped)
+    raise ValueError("segment ids must be integers")
 
 
 class ExportAnnotatedConfigContract(BaseModel):
     model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
 
     output_path: Path = Path("frames.csv")
     output_dir: Path
     output_format: Literal["csv", "json"] = "csv"
     export_profile: Literal["legacy_table_v1", "pts_dataset_v1"] = "pts_dataset_v1"
 
     video_id: int
     label_id: int | None = None
     information_source_name: str | None = None
     only_true: bool | None = None
     limit: int | None = None
 
     load_base_data: bool = False
     export_videos: bool = False
     export_frames: bool = True
 
     transcode_frames: bool = False
     transcode_fps: float = 50.0
     transcode_quality: int = 2
     transcode_ext: str = "jpg"
     transcode_overwrite: bool = False
     use_frame_pk_paths: bool = False
 
     use_export_flags: bool = True
     segment_ids: list[int] = Field(default_factory=list)
 
     center_key: str = ""
     all_centers: bool = False
     only_validated: bool = True
 
     @field_validator(
         "only_true",
         "load_base_data",
         "export_videos",
         "export_frames",
         "transcode_frames",
         "transcode_overwrite",
         "use_frame_pk_paths",
         "use_export_flags",
         "all_centers",
         "only_validated",
         mode="before",
     )
     @classmethod
-    def normalize_bool_string(cls, value: object) -> object:
+    def normalize_bool_string(
+        cls, value: bool | str | int | float | None
+    ) -> bool | str | int | None:
         if isinstance(value, bool):
             return value
 
         if isinstance(value, str):
             normalized = value.strip().lower()
             if normalized in {"1", "true", "yes", "on"}:
                 return True
             if normalized in {"0", "false", "no", "off", ""}:
                 return False
 
+        if isinstance(value, float):
+            if not value.is_integer():
+                raise ValueError("Boolean-like number must be 0 or 1.")
+            return bool(int(value))
+        if isinstance(value, int):
+            return bool(value)
         return value
 
     @field_validator("center_key", mode="before")
     @classmethod
-    def normalize_center_key(cls, value: object) -> object:
+    def normalize_center_key(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value.strip()
-        return value
+        return str(value)
 
     @field_validator("information_source_name", "transcode_ext", mode="before")
     @classmethod
-    def normalize_required_string(cls, value: object) -> object:
+    def normalize_required_string(
+        cls, value: str | int | float | bool | None
+    ) -> str | None:
         if value is None:
             return value
         if isinstance(value, str):
             return value.strip()
-        return value
+        return str(value).strip()
 
     @field_validator("output_path", "output_dir", mode="before")
     @classmethod
-    def normalize_path(cls, value: object) -> object:
+    def normalize_path(
+        cls, value: Path | str | None
+    ) -> Path | str | None:
         if isinstance(value, Path):
             return value
         if isinstance(value, str):
             stripped = value.strip()
             if not stripped:
                 return value
             return Path(stripped)
         return value
 
     @field_validator("segment_ids", mode="before")
     @classmethod
-    def normalize_segment_ids(cls, value: object) -> object:
+    def normalize_segment_ids(
+        cls, value: Sequence[object] | str | None
+    ) -> Sequence[int] | list[int]:
         if value in (None, ""):
             return []
-        return value
+        if isinstance(value, str):
+            parts = [part.strip() for part in value.split(",")]
+            return [_coerce_int(part) for part in parts if part]
+        if isinstance(value, (bytes, bytearray)):
+            raise ValueError("segment_ids cannot be bytes.")
+        if isinstance(value, Sequence):
+            return [_coerce_int(item) for item in value]
+        raise ValueError("segment_ids must be a sequence of integers.")
 
     @model_validator(mode="after")
     def validate_required_values(self) -> ExportAnnotatedConfigContract:
         if not self.output_dir:
             raise ValueError("output_dir is required")
 
         if self.video_id <= 0:
             raise ValueError("video_id must be a positive integer")
 
         if self.label_id is not None and self.label_id <= 0:
             raise ValueError("label_id must be a positive integer")
 
         if self.limit is not None and self.limit <= 0:
             raise ValueError("limit must be a positive integer")
 
         if not self.transcode_ext:
             raise ValueError("transcode_ext is required")
 
         if self.center_key and self.all_centers:
             raise ValueError(
                 "Export scope must use center_key or all_centers, not both"
             )
 
         return self
 
     @property
     def resolved_output_path(self) -> Path:
         if not self.output_path.is_absolute():
             return self.output_dir / self.output_path
         return self.output_path
 
     @property
     def resolved_output_dir(self) -> Path:
         return self.output_dir
 
     @classmethod
-    def from_api_payload(cls, payload: dict[str, Any]) -> ExportAnnotatedConfigContract:
+    def from_api_payload(
+        cls, payload: Mapping[str, JsonValue]
+    ) -> ExportAnnotatedConfigContract:
         raw = dict(payload)
 
         config_path = raw.pop("config_path", None)
         if config_path:
             config_data = cls._load_yaml_payload(Path(str(config_path)))
             config_data.update(raw)
             raw = config_data
 
         return cls.model_validate(raw)
 
     @staticmethod
-    def _load_yaml_payload(config_path: Path) -> dict[str, Any]:
+    def _load_yaml_payload(config_path: Path) -> dict[str, JsonValue]:
         if not config_path.exists():
             raise FileNotFoundError(f"config file not found: {config_path}")
 
         loaded = yaml.safe_load(config_path.read_text())
 
         if loaded is None:
             return {}
 
         if not isinstance(loaded, dict):
             raise ValueError("export config must be a mapping/object")
 
-        return cast(dict[str, Any], loaded)
+        return cast(dict[str, JsonValue], loaded)
 
     def to_export_config(self) -> export_config:
         return export_config(
             output_path=self.output_path,
             output_dir=self.output_dir,
             output_format=self.output_format,
             export_profile=self.export_profile,
             video_id=self.video_id,
             label_id=self.label_id,
             information_source_name=self.information_source_name,
             only_true=self.only_true,
             limit=self.limit,
             load_base_data=self.load_base_data,
             export_videos=self.export_videos,
             export_frames=self.export_frames,
             transcode_frames=self.transcode_frames,
             transcode_fps=self.transcode_fps,
             transcode_quality=self.transcode_quality,
             transcode_ext=self.transcode_ext,
             transcode_overwrite=self.transcode_overwrite,
             use_frame_pk_paths=self.use_frame_pk_paths,
             use_export_flags=self.use_export_flags,
             segment_ids=self.segment_ids,
             center_key=self.center_key or None,
             all_centers=self.all_centers,
             only_validated=self.only_validated,
         )
diff --git lx_dtypes/models/contracts/export_ready.py lx_dtypes/models/contracts/export_ready.py
index 135a12d..d1df810 100644
--- lx_dtypes/models/contracts/export_ready.py
+++ lx_dtypes/models/contracts/export_ready.py
@@ -1,70 +1,72 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from dataclasses import dataclass
 from typing import TypedDict
 
 from pydantic import BaseModel, ConfigDict, field_validator
+from lx_dtypes.models.contracts.json_types import JsonValue
 
 
 class VideoReadyForExportData(TypedDict):
     center_key: str | None
     processed_file_sha256: str | None
 
 
 class VideoReadyForExportPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     center_key: str | None = None
     processed_file_sha256: str | None = None
 
     @field_validator("center_key", "processed_file_sha256", mode="before")
     @classmethod
-    def blank_to_none(cls, value: object) -> object:
+    def blank_to_none(cls, value: str | int | float | bool | None) -> str | None:
         if value is None:
             return None
         if isinstance(value, str):
             return value.strip() or None
-        return value
+        converted = str(value).strip()
+        return converted or None
 
 
 @dataclass(frozen=True, slots=True)
 class ReadyForExportResult:
     video_id: int
     ready_for_export: bool
     ready_for_export_at: str | None
     ready_for_export_by: str
     processed_file_sha256: str
 
-    def to_dict(self) -> dict[str, object | None]:
+    def to_dict(self) -> dict[str, JsonValue | None]:
         return {
             "video_id": self.video_id,
             "ready_for_export": self.ready_for_export,
             "ready_for_export_at": self.ready_for_export_at,
             "ready_for_export_by": self.ready_for_export_by,
             "processed_file_sha256": self.processed_file_sha256,
         }
 
 
 def validate_video_ready_for_export_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> VideoReadyForExportPayload:
     return VideoReadyForExportPayload.model_validate(dict(payload))
 
 
 def dump_video_ready_for_export_payload(
     payload: VideoReadyForExportPayload,
 ) -> VideoReadyForExportData:
     return {
         "center_key": payload.center_key,
         "processed_file_sha256": payload.processed_file_sha256,
     }
 
 
 __all__ = [
     "ReadyForExportResult",
     "VideoReadyForExportData",
     "VideoReadyForExportPayload",
     "dump_video_ready_for_export_payload",
     "validate_video_ready_for_export_payload",
 ]
diff --git lx_dtypes/models/contracts/finding_classification.py lx_dtypes/models/contracts/finding_classification.py
index 5a8fd4d..54a261d 100644
--- lx_dtypes/models/contracts/finding_classification.py
+++ lx_dtypes/models/contracts/finding_classification.py
@@ -1,46 +1,47 @@
 from __future__ import annotations
 
 from pydantic import BaseModel, ConfigDict, Field
+from lx_dtypes.models.contracts.json_types import JsonValue
 
 
 class FindingClassificationTypeCore(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str
     description: str = ""
 
 
 class FindingClassificationCore(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str
     description: str = ""
 
 
 class FindingClassificationChoiceCore(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str
     description: str = ""
-    subcategories: dict[str, object] = Field(default_factory=dict)
-    numerical_descriptors: dict[str, object] = Field(default_factory=dict)
+    subcategories: dict[str, JsonValue] = Field(default_factory=dict)
+    numerical_descriptors: dict[str, JsonValue] = Field(default_factory=dict)
 
 
 class PatientFindingClassificationCore(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     patient_finding_id: int
     finding_name: str
     classification_name: str
     classification_choice_name: str
     is_active: bool = True
-    subcategories: dict[str, object] = Field(default_factory=dict)
-    numerical_descriptors: dict[str, object] = Field(default_factory=dict)
+    subcategories: dict[str, JsonValue] = Field(default_factory=dict)
+    numerical_descriptors: dict[str, JsonValue] = Field(default_factory=dict)
 
 
 __all__ = [
     "FindingClassificationTypeCore",
     "FindingClassificationCore",
     "FindingClassificationChoiceCore",
     "PatientFindingClassificationCore",
 ]
diff --git lx_dtypes/models/contracts/frame_annotation.py lx_dtypes/models/contracts/frame_annotation.py
index b3abf41..8127d7e 100644
--- lx_dtypes/models/contracts/frame_annotation.py
+++ lx_dtypes/models/contracts/frame_annotation.py
@@ -1,162 +1,164 @@
 # /lx-data-models/lx_dtypes/models/contracts/frame_annotation.py
 from __future__ import annotations
 
-from typing import Any, Literal, cast
+from typing import Literal, cast
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 from lx_dtypes.models.contracts.information_source import normalize_name_reference
 from lx_dtypes.models.contracts.json_types import JsonObject
 
 
 class FrameAnnotationLabelOptionPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     id: int
     name: str
 
 
 class FrameAnnotationQueueSpecPayload(BaseModel):
     """
     Validated transport contract for frame annotation queue requests.
 
     This payload intentionally contains only JSON/task-safe values:
     primitive IDs, strings, booleans, and sets of IDs. Django ORM objects are
     resolved by the endoreg_db adapter layer.
     """
 
     model_config = ConfigDict(
         extra="forbid",
         frozen=True,
         arbitrary_types_allowed=True,
     )
 
     limit: int
     task_mode: str = "random"
     video_id: int | None = None
     label_set_id: int | None = None
     target_label_id: int | None = None
     filter_label_id: int | None = None
     information_source_name: str = "manual_annotation"
     annotator: str = ""
     exclude_annotated: bool = True
     ai_dataset_id: int | None = None
     sampling_strategy: str = "balanced"
     prediction_segments_only: bool = True
     exclude_frame_ids: set[int] = Field(default_factory=set)
     require_extracted_frames: bool = True
     require_raw_video: bool = False
     require_processed_video: bool = False
     require_streamable_video_artifact: bool = False
 
     @field_validator("information_source_name", mode="before")
     @classmethod
-    def normalize_information_source_name(cls, value: object) -> str:
+    def normalize_information_source_name(
+        cls, value: str | None
+    ) -> str:
         return normalize_name_reference(value, default="manual_annotation")
 
 
 class FrameAnnotationAnnotationPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     id: int
     label_id: int
     label_name: str
     value: bool
     float_value: float | None = None
     annotator: str | None = None
     information_source_name: str | None = None
     model_meta_id: int | None = None
     external_annotation_id: str | None = None
 
 
 class FrameAnnotationTaskPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     frame_id: int
     video_id: int
     frame_number: int
     relative_path: str
     frame_stream_path: str
     annotation_mode: str = "multilabel"
     label_options: list[FrameAnnotationLabelOptionPayload] = Field(default_factory=list)
     manual_annotations: list[FrameAnnotationAnnotationPayload] = Field(
         default_factory=list
     )
     prediction_annotations: list[FrameAnnotationAnnotationPayload] = Field(
         default_factory=list
     )
     manual_positive_label_ids: list[int] = Field(default_factory=list)
     prediction_positive_label_ids: list[int] = Field(default_factory=list)
     suggested_label_ids: list[int] = Field(default_factory=list)
     dataset_selection_label_id: int | None = None
     dataset_selection_label_name: str | None = None
     dataset_selection_source: str | None = None
     dataset_bucket: str | None = None
     frame_file_type: str | None = None
     decoded_frame_stream_path: str | None = None
 
 
 class FrameAnnotationQueueResultPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     tasks: list[FrameAnnotationTaskPayload]
     selection_strategy: str
-    label_distribution: list[dict[str, Any]] = Field(default_factory=list)
+    label_distribution: list[JsonObject] = Field(default_factory=list)
     selected_label_counts: dict[str, int] = Field(default_factory=dict)
     segment_bucket_counts: dict[str, int] = Field(default_factory=dict)
     annotation_bucket_counts: dict[str, int] = Field(default_factory=dict)
     bucket_counts: dict[str, int] = Field(default_factory=dict)
 
 
 class FrameAnnotationRandomTaskResponsePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     status: Literal["success"] = "success"
     task: FrameAnnotationTaskPayload
     tasks: list[FrameAnnotationTaskPayload]
     count: int
     task_mode: str
     selection_strategy: str
     dataset_frame_filter: str
     prediction_segments_only: bool
     frame_file_type: str | None = None
     label_group_id: int | None = None
     target_label: str | None = None
     filter_label: str | None = None
     ai_dataset_id: int | None = None
     ai_dataset_name: str | None = None
     ai_dataset_type: str | None = None
     label_distribution: list[dict[str, int]] = Field(default_factory=list)
     selected_label_counts: dict[str, int] = Field(default_factory=dict)
     segment_bucket_counts: dict[str, int] = Field(default_factory=dict)
     annotation_bucket_counts: dict[str, int] = Field(default_factory=dict)
     bucket_counts: dict[str, int] = Field(default_factory=dict)
 
     def to_response_dict(self) -> JsonObject:
         return cast(JsonObject, self.model_dump(mode="json", exclude_none=True))
 
 
 class FrameAnnotationSkipResponsePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     status: Literal["success"] = "success"
     skipped_frame_id: int
     video_id: int
     annotator: str
     reason: str
     pruned_unused_frames: int
     next_task: FrameAnnotationTaskPayload | None = None
 
     def to_response_dict(self) -> JsonObject:
         return cast(JsonObject, self.model_dump(mode="json", exclude_none=True))
 
 
 __all__ = [
     "FrameAnnotationAnnotationPayload",
     "FrameAnnotationLabelOptionPayload",
     "FrameAnnotationQueueResultPayload",
     "FrameAnnotationQueueSpecPayload",
     "FrameAnnotationRandomTaskResponsePayload",
     "FrameAnnotationSkipResponsePayload",
     "FrameAnnotationTaskPayload",
 ]
diff --git lx_dtypes/models/contracts/hub_transfer.py lx_dtypes/models/contracts/hub_transfer.py
index d858c14..781ed5e 100644
--- lx_dtypes/models/contracts/hub_transfer.py
+++ lx_dtypes/models/contracts/hub_transfer.py
@@ -1,473 +1,478 @@
 from __future__ import annotations
 
-from typing import Any, Literal, NotRequired, TypedDict, cast
+from collections.abc import Mapping
+from typing import Literal, NotRequired, TypedDict, cast
 
 from pydantic import (
     BaseModel,
     ConfigDict,
     Field,
     ValidationError,
     field_validator,
     model_validator,
 )
 
 from .json_types import JsonValue
 
 type HubTransferJsonScalar = str | int | float | bool | None
 type HubTransferJsonValue = (
     HubTransferJsonScalar
     | list["HubTransferJsonValue"]
     | dict[str, "HubTransferJsonValue"]
 )
 type HubTransferJsonObject = dict[str, HubTransferJsonValue]
 type HubTransferSegmentSourceKind = Literal["manual_annotation", "prediction"]
 type HubTransferSegmentValidationState = Literal["unvalidated", "validated"]
 
 
 class HubTransferVideoFilePayloadData(TypedDict):
     video_hash: str
     processed_video_hash: str
     suffix: str | None
     fps: float | None
     duration: float | None
     frame_count: int | None
     width: int | None
     height: int | None
 
 
 class HubTransferSensitiveMetaPayloadData(TypedDict):
     patient_hash: str
     examination_hash: str
 
 
 class HubTransferVideoStatePayloadData(TypedDict, total=False):
     processing_started: bool
     frames_extracted: bool
     sensitive_meta_processed: bool
     frame_annotations_generated: bool
     anonymized: bool
     anonymization_validated: bool
     outside_segments_removed: bool
     segment_annotations_created: bool
     segment_annotations_validated: bool
     processed_file_sha256: str
 
 
 class HubTransferProcessingHistoryPayloadData(TypedDict):
     file_hash: str
     success: bool
 
 
 class HubTransferFrameAnnotationPayloadData(TypedDict):
     annotation_id: int | str
     video_hash: str
     frame_number: int
     frame_relative_path: str
     frame_timestamp: float | None
     label_name: str
     value: bool
     float_value: float | None
     information_source_name: str
 
 
 class HubTransferSegmentProvenancePayloadData(TypedDict):
     information_source_name: str
 
 
 class HubTransferVideoSegmentPayloadData(TypedDict):
     source_node_key: str
     source_segment_id: int | str
     video_hash: str
     start_frame_number: int
     end_frame_number_exclusive: int
     label_name: str
     source_kind: HubTransferSegmentSourceKind
     validation_state: HubTransferSegmentValidationState
     export_segment: bool
     anonymous_provenance: HubTransferSegmentProvenancePayloadData
     model_name: NotRequired[str]
     model_version: NotRequired[str]
 
 
 class HubTransferReportPayloadData(TypedDict):
     template_name: str
     template_version: str
     template_hash: str
     status: Literal["final"]
     version: int
     is_active: Literal[True]
 
 
 class HubTransferVideoResourceRowsData(TypedDict, total=False):
     video_file: HubTransferVideoFilePayloadData
     sensitive_meta: HubTransferSensitiveMetaPayloadData
     video_state: HubTransferVideoStatePayloadData
     processing_history: HubTransferProcessingHistoryPayloadData
     video_segments: list[HubTransferVideoSegmentPayloadData]
     frame_annotations: list[HubTransferFrameAnnotationPayloadData]
     reports: list[HubTransferReportPayloadData]
 
 
 class HubTransferRawPdfFilePayloadData(TypedDict):
     pdf_hash: str
     anonymized_text: str
 
 
 class HubTransferRawPdfStatePayloadData(TypedDict, total=False):
     processing_started: bool
     text_meta_extracted: bool
     sensitive_meta_processed: bool
     anonymized: bool
     anonymization_validated: bool
     processed_file_sha256: str
 
 
 class HubTransferReportResourceRowsData(TypedDict, total=False):
     raw_pdf_file: HubTransferRawPdfFilePayloadData
     sensitive_meta: HubTransferSensitiveMetaPayloadData
     raw_pdf_state: HubTransferRawPdfStatePayloadData
     processing_history: HubTransferProcessingHistoryPayloadData
     reports: list[HubTransferReportPayloadData]
 
 
 class HubTransferProcessingSnapshotData(TypedDict):
     sender_processing_success: bool
 
 
 class HubTransferProvenanceData(TypedDict, total=False):
     entrypoint: str
     source_node_key: str
     source_center_key: str
     target_node_key: str
     transfer_mode: str
     processing_policy: str
     cleanup_policy: str
 
 
 class HubTransferVideoTransferPayloadData(TypedDict):
     transfer_key: str
     source_node_key: str
     target_node_key: str
     source_center_key: str
     resource_kind: Literal["video"]
     resource_hash: str
     transfer_mode: str
     processing_policy: str
     processing_intent: str
     cleanup_policy: str
     payload_schema_version: Literal["3.0"]
     resource_rows: HubTransferVideoResourceRowsData
     processing_snapshot: HubTransferProcessingSnapshotData
     provenance: NotRequired[HubTransferProvenanceData]
 
 
 class HubTransferReportTransferPayloadData(TypedDict):
     transfer_key: str
     source_node_key: str
     target_node_key: str
     source_center_key: str
     resource_kind: Literal["report"]
     resource_hash: str
     transfer_mode: str
     processing_policy: str
     processing_intent: str
     cleanup_policy: str
     payload_schema_version: Literal["3.0"]
     resource_rows: HubTransferReportResourceRowsData
     processing_snapshot: HubTransferProcessingSnapshotData
     provenance: NotRequired[HubTransferProvenanceData]
 
 
 class _StrictPayload(BaseModel):
     model_config = ConfigDict(
         extra="forbid", frozen=True, strict=True, str_strip_whitespace=True
     )
 
 
 class HubTransferVideoFilePayload(_StrictPayload):
     video_hash: str = Field(min_length=1)
     processed_video_hash: str = Field(min_length=1)
     suffix: str | None = None
     fps: float | None = Field(default=None, ge=0)
     duration: float | None = Field(default=None, ge=0)
     frame_count: int | None = Field(default=None, ge=0)
     width: int | None = Field(default=None, ge=1)
     height: int | None = Field(default=None, ge=1)
 
 
 class HubTransferSensitiveMetaPayload(_StrictPayload):
     patient_hash: str = Field(min_length=1)
     examination_hash: str = Field(min_length=1)
 
 
 class HubTransferVideoStatePayload(_StrictPayload):
     processing_started: bool | None = None
     frames_extracted: bool | None = None
     sensitive_meta_processed: bool | None = None
     frame_annotations_generated: bool | None = None
     anonymized: bool | None = None
     anonymization_validated: bool | None = None
     outside_segments_removed: bool | None = None
     segment_annotations_created: bool | None = None
     segment_annotations_validated: bool | None = None
     processed_file_sha256: str | None = None
 
 
 class HubTransferProcessingHistoryPayload(_StrictPayload):
     file_hash: str = Field(min_length=1)
     success: bool
 
 
 class HubTransferFrameAnnotationPayload(_StrictPayload):
     annotation_id: int | str
     video_hash: str = Field(min_length=1)
     frame_number: int = Field(ge=0)
     frame_relative_path: str = Field(min_length=1)
     frame_timestamp: float | None = Field(default=None, ge=0)
     label_name: str = Field(min_length=1)
     value: bool
     float_value: float | None = None
     information_source_name: str = Field(min_length=1)
 
 
 class HubTransferSegmentProvenancePayload(_StrictPayload):
     information_source_name: str = Field(min_length=1)
 
 
 class HubTransferVideoSegmentPayload(_StrictPayload):
     source_node_key: str = Field(min_length=1)
     source_segment_id: int | str
     video_hash: str = Field(min_length=1)
     start_frame_number: int = Field(ge=0)
     end_frame_number_exclusive: int = Field(ge=1)
     label_name: str = Field(min_length=1)
     source_kind: HubTransferSegmentSourceKind
     validation_state: HubTransferSegmentValidationState
     export_segment: bool
     anonymous_provenance: HubTransferSegmentProvenancePayload
     model_name: str | None = None
     model_version: str | None = None
 
     @field_validator("source_segment_id", mode="before")
     @classmethod
-    def _normalize_source_segment_id(cls, value: object) -> int | str:
+    def _normalize_source_segment_id(cls, value: int | str) -> int | str:
         if isinstance(value, bool) or not isinstance(value, (int, str)):
             raise ValueError("source_segment_id must be an integer or string")
         if isinstance(value, str):
             normalized = value.strip()
             if not normalized:
                 raise ValueError("source_segment_id must not be blank")
             return normalized
         return value
 
     @model_validator(mode="after")
     def _validate_segment_contract(self) -> "HubTransferVideoSegmentPayload":
         if self.end_frame_number_exclusive <= self.start_frame_number:
             raise ValueError(
                 "end_frame_number_exclusive must exceed start_frame_number"
             )
         has_model_name = self.model_name is not None
         has_model_version = self.model_version is not None
         if has_model_name != has_model_version:
             raise ValueError("model_name and model_version must be supplied together")
         if has_model_name and (
             self.source_kind != "prediction" or not self.export_segment
         ):
             raise ValueError(
                 "model metadata is permitted only for exported prediction segments"
             )
         return self
 
 
 class HubTransferReportPayload(_StrictPayload):
     template_name: str = Field(min_length=1)
     template_version: str = ""
     template_hash: str = ""
     status: Literal["final"] = "final"
     version: int = Field(default=1, ge=1)
     is_active: Literal[True] = True
 
 
 class HubTransferVideoResourceRowsPayload(_StrictPayload):
     video_file: HubTransferVideoFilePayload
     sensitive_meta: HubTransferSensitiveMetaPayload
     video_state: HubTransferVideoStatePayload
     processing_history: HubTransferProcessingHistoryPayload
     video_segments: list[HubTransferVideoSegmentPayload] = Field(default_factory=list)
     frame_annotations: list[HubTransferFrameAnnotationPayload] = Field(
         default_factory=list
     )
     reports: list[HubTransferReportPayload] = Field(default_factory=list)
 
 
 class HubTransferRawPdfFilePayload(_StrictPayload):
     pdf_hash: str = Field(min_length=1)
     anonymized_text: str = Field(min_length=1)
 
 
 class HubTransferRawPdfStatePayload(_StrictPayload):
     processing_started: bool | None = None
     text_meta_extracted: bool | None = None
     sensitive_meta_processed: bool | None = None
     anonymized: bool | None = None
     anonymization_validated: bool | None = None
     processed_file_sha256: str | None = None
 
 
 class HubTransferReportResourceRowsPayload(_StrictPayload):
     raw_pdf_file: HubTransferRawPdfFilePayload
     sensitive_meta: HubTransferSensitiveMetaPayload
     raw_pdf_state: HubTransferRawPdfStatePayload
     processing_history: HubTransferProcessingHistoryPayload
     reports: list[HubTransferReportPayload] = Field(default_factory=list)
 
 
 class HubTransferProcessingSnapshotPayload(_StrictPayload):
     sender_processing_success: bool
 
 
 class HubTransferProvenancePayload(_StrictPayload):
     entrypoint: str | None = None
     source_node_key: str | None = None
     source_center_key: str | None = None
     target_node_key: str | None = None
     transfer_mode: str | None = None
     processing_policy: str | None = None
     cleanup_policy: str | None = None
 
 
 class _HubTransferPayload(_StrictPayload):
     transfer_key: str = Field(min_length=1)
     source_node_key: str = Field(min_length=1)
     target_node_key: str = Field(min_length=1)
     source_center_key: str = Field(min_length=1)
     resource_hash: str = Field(min_length=1)
     transfer_mode: str = Field(min_length=1)
     processing_policy: str = Field(min_length=1)
     processing_intent: str = Field(min_length=1)
     cleanup_policy: str = Field(min_length=1)
     payload_schema_version: Literal["3.0"]
     processing_snapshot: HubTransferProcessingSnapshotPayload
     provenance: HubTransferProvenancePayload | None = None
 
 
 class HubTransferVideoTransferPayload(_HubTransferPayload):
     resource_kind: Literal["video"]
     resource_rows: HubTransferVideoResourceRowsPayload
 
     @model_validator(mode="after")
     def _validate_resource_linkage(self) -> "HubTransferVideoTransferPayload":
         if self.resource_rows.video_file.video_hash != self.resource_hash:
             raise ValueError("video_file.video_hash must match resource_hash")
         for segment in self.resource_rows.video_segments:
             if segment.source_node_key != self.source_node_key:
                 raise ValueError("video segment source_node_key must match transfer")
             if segment.video_hash != self.resource_hash:
                 raise ValueError("video segment video_hash must match resource_hash")
         return self
 
 
 class HubTransferReportTransferPayload(_HubTransferPayload):
     resource_kind: Literal["report"]
     resource_rows: HubTransferReportResourceRowsPayload
 
 
-def _validate_payload(model_cls: type[BaseModel], value: Any) -> dict[str, JsonValue]:
+def _validate_payload(
+    model_cls: type[BaseModel], value: Mapping[str, JsonValue]
+) -> HubTransferJsonObject:
     if not isinstance(value, dict):
         raise ValueError("payload must be a JSON object")
     try:
         model = model_cls.model_validate(value)
     except ValidationError as exc:
         raise ValueError(str(exc)) from exc
-    return cast(dict[str, JsonValue], model.model_dump(mode="json", exclude_none=True))
+    return cast(
+        HubTransferJsonObject, model.model_dump(mode="json", exclude_none=True)
+    )
 
 
 def validate_hub_transfer_video_resource_rows(
-    value: Any,
+    value: Mapping[str, JsonValue],
 ) -> HubTransferVideoResourceRowsData:
     return cast(
         HubTransferVideoResourceRowsData,
         _validate_payload(HubTransferVideoResourceRowsPayload, value),
     )
 
 
 def validate_hub_transfer_report_resource_rows(
-    value: Any,
+    value: Mapping[str, JsonValue],
 ) -> HubTransferReportResourceRowsData:
     return cast(
         HubTransferReportResourceRowsData,
         _validate_payload(HubTransferReportResourceRowsPayload, value),
     )
 
 
 def validate_hub_transfer_processing_snapshot(
-    value: Any,
+    value: Mapping[str, JsonValue],
 ) -> HubTransferProcessingSnapshotData:
     return cast(
         HubTransferProcessingSnapshotData,
         _validate_payload(HubTransferProcessingSnapshotPayload, value),
     )
 
 
 def validate_hub_transfer_video_payload(
-    value: Any,
+    value: Mapping[str, JsonValue],
 ) -> HubTransferVideoTransferPayloadData:
     return cast(
         HubTransferVideoTransferPayloadData,
         _validate_payload(HubTransferVideoTransferPayload, value),
     )
 
 
 def validate_hub_transfer_report_payload(
-    value: Any,
+    value: Mapping[str, JsonValue],
 ) -> HubTransferReportTransferPayloadData:
     return cast(
         HubTransferReportTransferPayloadData,
         _validate_payload(HubTransferReportTransferPayload, value),
     )
 
 
 __all__ = [
     "HubTransferFrameAnnotationPayload",
     "HubTransferFrameAnnotationPayloadData",
     "HubTransferJsonObject",
     "HubTransferJsonScalar",
     "HubTransferJsonValue",
     "HubTransferProcessingHistoryPayload",
     "HubTransferProcessingHistoryPayloadData",
     "HubTransferProcessingSnapshotData",
     "HubTransferProcessingSnapshotPayload",
     "HubTransferProvenanceData",
     "HubTransferProvenancePayload",
     "HubTransferRawPdfFilePayload",
     "HubTransferRawPdfFilePayloadData",
     "HubTransferRawPdfStatePayload",
     "HubTransferRawPdfStatePayloadData",
     "HubTransferReportPayload",
     "HubTransferReportPayloadData",
     "HubTransferReportResourceRowsData",
     "HubTransferReportResourceRowsPayload",
     "HubTransferReportTransferPayload",
     "HubTransferReportTransferPayloadData",
     "HubTransferSegmentProvenancePayload",
     "HubTransferSegmentProvenancePayloadData",
     "HubTransferSegmentSourceKind",
     "HubTransferSegmentValidationState",
     "HubTransferSensitiveMetaPayload",
     "HubTransferSensitiveMetaPayloadData",
     "HubTransferVideoFilePayload",
     "HubTransferVideoFilePayloadData",
     "HubTransferVideoResourceRowsData",
     "HubTransferVideoResourceRowsPayload",
     "HubTransferVideoSegmentPayload",
     "HubTransferVideoSegmentPayloadData",
     "HubTransferVideoStatePayload",
     "HubTransferVideoStatePayloadData",
     "HubTransferVideoTransferPayload",
     "HubTransferVideoTransferPayloadData",
     "validate_hub_transfer_processing_snapshot",
     "validate_hub_transfer_report_payload",
     "validate_hub_transfer_report_resource_rows",
     "validate_hub_transfer_video_payload",
     "validate_hub_transfer_video_resource_rows",
 ]
diff --git lx_dtypes/models/contracts/image_processing.py lx_dtypes/models/contracts/image_processing.py
index 4ed8129..9fd5108 100644
--- lx_dtypes/models/contracts/image_processing.py
+++ lx_dtypes/models/contracts/image_processing.py
@@ -1,40 +1,41 @@
 from __future__ import annotations
 
 from pathlib import Path
 
 from pydantic import BaseModel, ConfigDict, Field
+from lx_dtypes.models.contracts.json_types import JsonValue
 
 
 def _empty_combined_results() -> list[
     tuple[str, tuple[int, int, int, int], float, list[tuple[str, str]]]
 ]:
     return []
 
 
 def _empty_modified_images() -> dict[tuple[str, str], str]:
     return {}
 
 
 class ImageProcessingResultPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True)
 
     filename: Path
     file_type: str
     extracted_text: str
     names_detected: list[str] = Field(default_factory=list)
     combined_results: list[
         tuple[
             str,
             tuple[int, int, int, int],
             float,
             list[tuple[str, str]],
         ]
     ] = Field(default_factory=_empty_combined_results)
     modified_images_map: dict[tuple[str, str], str] = Field(
         default_factory=_empty_modified_images
     )
     gender_pars: list[str] = Field(default_factory=list)
-    llm_results: dict[str, object] = Field(default_factory=dict)
+    llm_results: dict[str, JsonValue] = Field(default_factory=dict)
 
 
 __all__ = ["ImageProcessingResultPayload"]
diff --git lx_dtypes/models/contracts/information_source.py lx_dtypes/models/contracts/information_source.py
index 8548f8f..6c5085b 100644
--- lx_dtypes/models/contracts/information_source.py
+++ lx_dtypes/models/contracts/information_source.py
@@ -1,26 +1,26 @@
 from __future__ import annotations
 
 from typing import Protocol, runtime_checkable
 from pydantic import BaseModel, ConfigDict, field_validator
 
 
 @runtime_checkable
 class NamedObject(Protocol):
     name: str
 
 
-def normalize_name_reference(value: object, *, default: str) -> str:
+def normalize_name_reference(value: str | NamedObject | None, *, default: str) -> str:
     source_value = getattr(value, "name", value)
     normalized = str(source_value or default).strip()
     return normalized or default
 
 
 class InformationSourceRef(BaseModel):
     model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
 
     name: str
 
     @field_validator("name", mode="before")
     @classmethod
-    def normalize(cls, value: object) -> str:
+    def normalize(cls, value: str | NamedObject | None) -> str:
         return normalize_name_reference(value, default="manual_annotation")
diff --git lx_dtypes/models/contracts/knowledge_base.py lx_dtypes/models/contracts/knowledge_base.py
index f2094ed..c461ab9 100644
--- lx_dtypes/models/contracts/knowledge_base.py
+++ lx_dtypes/models/contracts/knowledge_base.py
@@ -1,52 +1,54 @@
 from __future__ import annotations
 
-from typing import TYPE_CHECKING, Any, Protocol
+from typing import TYPE_CHECKING, Protocol
+
+from lx_dtypes.models.contracts.json_types import JsonObject
 
 if TYPE_CHECKING:
     from lx_dtypes.models.ledger.p_examination.Pydantic import PExamination
 
 
 class KnowledgeBaseContract(Protocol):
     """Typed API surface used by patient and reporting endpoints."""
 
-    report_template: dict[str, Any]
-    findings_validator: dict[str, Any]
-    classification_validator: dict[str, Any]
-    intervention_validator: dict[str, Any]
-    unit_validator: dict[str, Any]
-    examination_validator: dict[str, Any]
+    report_template: JsonObject
+    findings_validator: JsonObject
+    classification_validator: JsonObject
+    intervention_validator: JsonObject
+    unit_validator: JsonObject
+    examination_validator: JsonObject
 
-    def export_core_concepts(self) -> dict[str, Any]: ...
+    def export_core_concepts(self) -> JsonObject: ...
 
-    def export_report_template(self, name: str) -> dict[str, Any]: ...
+    def export_report_template(self, name: str) -> JsonObject: ...
 
-    def export_report_template_preview(self, name: str) -> dict[str, Any]: ...
+    def export_report_template_preview(self, name: str) -> JsonObject: ...
 
     def get_report_template_lifecycle_status(self, name: str) -> str: ...
 
     def evaluate_report_template_validators(
         self, name: str, p_examination: "PExamination"
-    ) -> dict[str, Any]: ...
+    ) -> JsonObject: ...
 
     def evaluate_findings_validator(
         self, name: str, p_examination: "PExamination"
-    ) -> dict[str, Any]: ...
+    ) -> JsonObject: ...
 
     def evaluate_classification_validator(
         self, name: str, p_examination: "PExamination"
-    ) -> dict[str, Any]: ...
+    ) -> JsonObject: ...
 
     def evaluate_intervention_validator(
         self, name: str, p_examination: "PExamination"
-    ) -> dict[str, Any]: ...
+    ) -> JsonObject: ...
 
     def evaluate_unit_validator(
         self, name: str, p_examination: "PExamination"
-    ) -> dict[str, Any]: ...
+    ) -> JsonObject: ...
 
     def evaluate_examination_validator(
         self, name: str, p_examination: "PExamination"
-    ) -> dict[str, Any]: ...
+    ) -> JsonObject: ...
 
 
 __all__ = ["KnowledgeBaseContract"]
diff --git lx_dtypes/models/contracts/label_video_segment_serializer.py lx_dtypes/models/contracts/label_video_segment_serializer.py
index 1c0020b..962f4ad 100644
--- lx_dtypes/models/contracts/label_video_segment_serializer.py
+++ lx_dtypes/models/contracts/label_video_segment_serializer.py
@@ -1,49 +1,50 @@
 from __future__ import annotations
 
 from pydantic import BaseModel, ConfigDict, Field
+from lx_dtypes.models.contracts.json_types import JsonObject
 
 
 class LabelVideoSegmentSummaryPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     id: int
     label_id: int | None = None
     label_name: str
     source_name: str | None = None
     segment_origin: str
     prediction_meta_id: int | None = None
     start_frame_number: int
     end_frame_number: int
     start_time: float | None = None
     end_time: float | None = None
     export_segment: bool = False
 
 
 class LabelVideoSegmentFrameClassificationPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     frame_filename: str
     frame_file_path: str
     frame_url: str
-    all_classifications: list[dict[str, object]] = Field(default_factory=list)
+    all_classifications: list[JsonObject] = Field(default_factory=list)
     frame_id: int
 
 
 class LabelVideoSegmentTimeSegmentPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     segment_id: int
     segment_start: int
     segment_end: int
     start_time: float | None = None
     end_time: float | None = None
     frames: list[LabelVideoSegmentFrameClassificationPayload] = Field(
         default_factory=list
     )
 
 
 __all__ = [
     "LabelVideoSegmentFrameClassificationPayload",
     "LabelVideoSegmentSummaryPayload",
     "LabelVideoSegmentTimeSegmentPayload",
 ]
diff --git lx_dtypes/models/contracts/management_command.py lx_dtypes/models/contracts/management_command.py
index cbdde2a..3f8b44a 100644
--- lx_dtypes/models/contracts/management_command.py
+++ lx_dtypes/models/contracts/management_command.py
@@ -1,626 +1,635 @@
 from __future__ import annotations
 
 from pathlib import Path
+from collections.abc import Sequence
 from typing import Literal
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 from lx_dtypes.models.contracts.json_types import JsonObject
 
 
 type FrameSegmentReconciliationTrack = Literal["all", "manual", "prediction"]
 
 
 class VerboseManagementCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     verbose: bool
 
 
 class ModelInputCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     dataset_id: int = Field(gt=0)
     annotation_source_scope: str
     backbone_checkpoint: str = ""
     backbone_name: str
     epochs: int = Field(gt=0)
 
     @field_validator("annotation_source_scope", "backbone_name", mode="before")
     @classmethod
-    def normalize_required_text(cls, value: object) -> str:
+    def normalize_required_text(cls, value: str | int | float | bool | None) -> str:
         if isinstance(value, str):
             normalized = value.strip()
             if normalized:
                 return normalized
         raise ValueError("command option must be a non-empty string")
 
     @field_validator("backbone_checkpoint", mode="before")
     @classmethod
-    def normalize_backbone_checkpoint(cls, value: object) -> str:
+    def normalize_backbone_checkpoint(
+        cls, value: str | int | float | bool | None
+    ) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value
         raise ValueError("backbone_checkpoint must be a string")
 
 
 class ModelTrainingResultPayload(BaseModel):
     model_config = ConfigDict(extra="allow", frozen=True, strict=True)
 
     model_path: str
 
     @field_validator("model_path", mode="before")
     @classmethod
-    def normalize_model_path(cls, value: object) -> str:
+    def normalize_model_path(cls, value: str | None) -> str:
         if isinstance(value, str) and value.strip():
             return value
         raise ValueError("model_path must be a non-empty string")
 
 
 def validate_model_training_result(value: JsonObject) -> ModelTrainingResultPayload:
     return ModelTrainingResultPayload.model_validate(value)
 
 
 class TrainImageMultilabelModelCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     dataset_id: int = Field(gt=0)
     annotation_source_scope: str
     backbone_name: str
     backbone_checkpoint: str = ""
     epochs: int = Field(gt=0)
     batch_size: int = Field(gt=0)
     labelset_version: int = Field(gt=0)
     device: str
     freeze_backbone: bool
     treat_unlabeled_as_negative: bool
 
     @field_validator(
         "annotation_source_scope",
         "backbone_name",
         "device",
         mode="before",
     )
     @classmethod
-    def normalize_required_text(cls, value: object) -> str:
+    def normalize_required_text(cls, value: str | int | float | bool | None) -> str:
         if isinstance(value, str):
             normalized = value.strip()
             if normalized:
                 return normalized
         raise ValueError("command option must be a non-empty string")
 
     @field_validator("backbone_checkpoint", mode="before")
     @classmethod
-    def normalize_backbone_checkpoint(cls, value: object) -> str:
+    def normalize_backbone_checkpoint(
+        cls, value: str | int | float | bool | None
+    ) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value
         raise ValueError("backbone_checkpoint must be a string")
 
 
 class TrainPhiRegionDetectorCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     dataset_yaml: Path
     output_dir: Path
     base_model: str
     run_name: str = ""
     epochs: int = Field(gt=0)
     batch_size: int = Field(gt=0)
     input_size: int = Field(gt=0)
     device: str
     workers: int = Field(ge=0)
     patience: int = Field(ge=0)
     confidence_threshold: float = Field(ge=0.0, le=1.0)
     nms_threshold: float = Field(ge=0.0, le=1.0)
     class_ids: str = ""
     export_onnx: bool
 
     @field_validator("base_model", "device", mode="before")
     @classmethod
-    def normalize_required_text(cls, value: object) -> str:
+    def normalize_required_text(cls, value: str | int | float | bool | None) -> str:
         if isinstance(value, str):
             normalized = value.strip()
             if normalized:
                 return normalized
         raise ValueError("command option must be a non-empty string")
 
     @field_validator("run_name", "class_ids", mode="before")
     @classmethod
-    def normalize_optional_text(cls, value: object) -> str:
+    def normalize_optional_text(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value.strip()
         raise ValueError("command option must be a string")
 
 
 type TranscodeVideoQualityMode = Literal["fast", "balanced", "quality"]
 
 
 class TranscodeVideoCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(
         extra="ignore", frozen=True, strict=True, populate_by_name=True
     )
 
     input_dir: str
     output_dir: str
     filename: str = ""
     recursive: bool
     overwrite: bool
     dry_run: bool
     allow_unmanaged_output: bool
     force_cpu: bool
     quality_mode: TranscodeVideoQualityMode
     extension: tuple[str, ...] = Field(default_factory=tuple)
     fail_on_skipped: bool
     json_output: bool = Field(validation_alias="json", serialization_alias="json")
 
     @field_validator("input_dir", "output_dir", mode="before")
     @classmethod
-    def normalize_required_path_text(cls, value: object) -> str:
+    def normalize_required_path_text(
+        cls, value: str | Path | None
+    ) -> str:
         if isinstance(value, Path):
             return str(value)
         if isinstance(value, str):
             normalized = value.strip()
             if normalized:
                 return normalized
         raise ValueError("path command option must be a non-empty string")
 
     @field_validator("filename", mode="before")
     @classmethod
-    def normalize_filename(cls, value: object) -> str:
+    def normalize_filename(cls, value: str | Path | None) -> str:
         if value is None:
             return ""
         if isinstance(value, Path):
             return str(value)
         if isinstance(value, str):
             return value.strip()
         raise ValueError("filename must be a string")
 
     @field_validator("extension", mode="before")
     @classmethod
-    def normalize_extensions(cls, value: object) -> tuple[str, ...]:
+    def normalize_extensions(
+        cls, value: Sequence[str] | tuple[str, ...] | list[str] | str | None
+    ) -> tuple[str, ...]:
         if value is None:
             return ()
         if isinstance(value, tuple) and all(isinstance(item, str) for item in value):
             return value
         if isinstance(value, list) and all(isinstance(item, str) for item in value):
             return tuple(value)
         raise ValueError("extension must be a string sequence")
 
 
 class ValidateRuntimeStorageContractCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(
         extra="ignore", frozen=True, strict=True, populate_by_name=True
     )
 
     json_output: bool = Field(validation_alias="json", serialization_alias="json")
 
 
 class RuntimeStorageContractPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     protected_root: str
     data_root: str
     protected_paths: dict[str, str]
     public_paths: dict[str, str]
     valid: bool
     violations: list[str] = Field(default_factory=list)
 
 
 type ValidateVideoFileStatus = Literal["accessible", "missing", "corrupted", "unknown"]
 
 
 class ValidateVideoFilesCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     video_id: int = Field(default=0, ge=0)
     verbose: bool
     fix_missing: bool = False
 
     @field_validator("video_id", mode="before")
     @classmethod
-    def normalize_video_id(cls, value: object) -> int:
+    def normalize_video_id(cls, value: int | str | None) -> int:
         if value is None:
             return 0
         if isinstance(value, int):
             return value
         raise ValueError("video_id must be an integer")
 
 
 class ValidateVideoFileStatusPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     video_id: int = Field(ge=0)
     video_uuid: str
     status: ValidateVideoFileStatus
     path: str = ""
     size_mb: float = Field(ge=0.0)
     error: str = ""
 
 
 class ReapQuarantineCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(
         extra="ignore", frozen=True, strict=True, populate_by_name=True
     )
 
     older_than_days: int = Field(ge=0)
     dry_run: bool
     confirm: bool
     json_output: bool = Field(validation_alias="json", serialization_alias="json")
 
 
 class ReapUploadJobSourcesCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     limit: int = Field(default=0, ge=0)
     repeat_until_empty: bool
 
     @field_validator("limit", mode="before")
     @classmethod
-    def normalize_limit(cls, value: object) -> int:
+    def normalize_limit(cls, value: int | None) -> int:
         if value is None:
             return 0
         if isinstance(value, int):
             return value
         raise ValueError("limit must be an integer")
 
 
 class ReconcileFrameSegmentAnnotationsCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(
         extra="ignore", frozen=True, strict=True, populate_by_name=True
     )
 
     video_ids: tuple[int, ...] = Field(default_factory=tuple)
     segment_ids: tuple[int, ...] = Field(default_factory=tuple)
     annotator: str = ""
     track: FrameSegmentReconciliationTrack
     apply_changes: bool
     json_output: bool = Field(validation_alias="json", serialization_alias="json")
 
     @field_validator("video_ids", "segment_ids", mode="before")
     @classmethod
-    def normalize_id_tuple(cls, value: object) -> tuple[int, ...]:
+    def normalize_id_tuple(cls, value: tuple[int, ...] | list[int] | None) -> tuple[int, ...]:
         if value is None:
             return ()
         if isinstance(value, tuple) and all(isinstance(item, int) for item in value):
             return value
         if isinstance(value, list) and all(isinstance(item, int) for item in value):
             return tuple(value)
         raise ValueError("id options must be integer sequences")
 
     @field_validator("annotator", mode="before")
     @classmethod
-    def normalize_annotator(cls, value: object) -> str:
+    def normalize_annotator(cls, value: str | None) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value.strip()
         raise ValueError("annotator must be a string")
 
 
 class ReconcileMediaIntegrityCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(
         extra="ignore", frozen=True, strict=True, populate_by_name=True
     )
 
     dry_run: bool
     json_output: bool = Field(validation_alias="json", serialization_alias="json")
     video_id: list[int] = Field(default_factory=list)
     check_frames: bool
     repair_frames: bool
     repair_frame: list[int] = Field(default_factory=list)
     check_ffmpeg_meta: bool
     repair_ffmpeg_meta: bool
     check_streamable_probe: bool
     cleanup_stale_artifacts: bool
 
     @field_validator("video_id", "repair_frame", mode="before")
     @classmethod
-    def normalize_id_list(cls, value: object) -> list[int]:
+    def normalize_id_list(cls, value: list[int] | None) -> list[int]:
         if value is None:
             return []
         if isinstance(value, list) and all(isinstance(item, int) for item in value):
             return value
         raise ValueError("id options must be lists of integers")
 
 
 class ReconcileSegmentValidationStateCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     video_ids: list[int] = Field(default_factory=list)
     queue_cleanup: bool
 
     @field_validator("video_ids", mode="before")
     @classmethod
-    def normalize_video_ids(cls, value: object) -> list[int]:
+    def normalize_video_ids(cls, value: list[int] | None) -> list[int]:
         if value is None:
             return []
         if isinstance(value, list) and all(isinstance(item, int) for item in value):
             return value
         raise ValueError("video_ids must be a list of integer primary keys")
 
 
 class ReconcileVideoFormatsCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(
         extra="ignore", frozen=True, strict=True, populate_by_name=True
     )
 
     root: list[str] = Field(default_factory=list)
     include_default_roots: bool
     no_default_roots: bool
     include_legacy_roots: bool
     extension: list[str] = Field(default_factory=list)
     dry_run: bool
     repair: bool
     in_place: bool
     allow_unmanaged_root: bool
     include_compliant: bool
     max_files: int = Field(default=0, ge=0)
     min_free_bytes: int = Field(ge=0)
     force_cpu: bool
     fail_on_non_compliant: bool
     json_output: bool = Field(validation_alias="json", serialization_alias="json")
 
     @field_validator("root", "extension", mode="before")
     @classmethod
-    def normalize_text_list(cls, value: object) -> list[str]:
+    def normalize_text_list(cls, value: list[str] | None) -> list[str]:
         if value is None:
             return []
         if isinstance(value, list) and all(isinstance(item, str) for item in value):
             return value
         raise ValueError("command option must be a list of strings")
 
     @field_validator("max_files", mode="before")
     @classmethod
-    def normalize_max_files(cls, value: object) -> int:
+    def normalize_max_files(cls, value: int | None) -> int:
         if value is None:
             return 0
         if isinstance(value, int):
             return value
         raise ValueError("max_files must be an integer")
 
 
 class RefreshAuditLedgerIntegrityCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     once: bool
     pretty: bool
     fail_on_non_verified: bool
 
 
 class RegisterAiModelCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     model_meta_path: str
 
     @field_validator("model_meta_path", mode="before")
     @classmethod
-    def normalize_model_meta_path(cls, value: object) -> str:
+    def normalize_model_meta_path(cls, value: str | None) -> str:
         if isinstance(value, str) and value.strip():
             return value
         raise ValueError("model_meta_path must be a non-empty string")
 
 
 class RegisterAiModelMetaPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     name: str
     version: str
     model_type: str
     labelset: str
     labelset_version: int
     weights_path: str
     description: str = ""
 
     @field_validator(
         "name",
         "version",
         "model_type",
         "labelset",
         "weights_path",
         mode="before",
     )
     @classmethod
-    def normalize_required_text(cls, value: object) -> str:
+    def normalize_required_text(cls, value: str | int | float | bool | None) -> str:
         if isinstance(value, int):
             return str(value)
         if isinstance(value, str):
             normalized = value.strip()
             if normalized:
                 return normalized
         raise ValueError("model metadata field must be a non-empty string")
 
     @field_validator("description", mode="before")
     @classmethod
-    def normalize_description(cls, value: object) -> str:
+    def normalize_description(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value.strip()
         raise ValueError("description must be a string")
 
 
 class SetupEndoregDbCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     skip_ai_setup: bool
     force_recreate: bool
     yaml_only: bool
 
 
 class ShowUrlsCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     format_style: str = ""
 
     @field_validator("format_style", mode="before")
     @classmethod
-    def normalize_format_style(cls, value: object) -> str:
+    def normalize_format_style(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value
         raise ValueError("format_style must be a string")
 
 
 class ShowUrlsRoutePayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     url: str = ""
     module: str = ""
     name: str = ""
     decorators: str = ""
 
     @field_validator("url", "module", "name", "decorators", mode="before")
     @classmethod
-    def normalize_route_text(cls, value: object) -> str:
+    def normalize_route_text(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value
         return str(value)
 
 
 class ShowUrlsRoutesPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     routes: list[ShowUrlsRoutePayload]
 
 
 class StorageManagementCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     dry_run: bool
     force: bool
     cleanup_frames: bool
     cleanup_old_videos: bool
     cleanup_uploads: bool
     cleanup_logs: bool
     max_age_days: int = Field(ge=0)
     emergency_threshold: float = Field(ge=0.0, le=100.0)
 
 
 class StorageManagementInfoPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     total_gb: float
     used_gb: float
     free_gb: float
     usage_percent: float
     project_storage_gb: float
     critical: bool
     warning: bool
 
 
 class MigrateDataDirCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     source_root: str
     dry_run: bool
     manifest_path: str
 
 
 class MigrateMediaStorageCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(
         extra="ignore", frozen=True, strict=True, populate_by_name=True
     )
 
     apply: bool
     limit: int | None = None
     repeat_until_empty: bool
     json_output: bool = Field(validation_alias="json", serialization_alias="json")
     fail_fast: bool
     include_raw: bool
     include_processed: bool
     include_reports: bool
     include_streamable: bool
     delete_verified_legacy: bool
     video_ids: list[int] = Field(default_factory=list)
     hash_value: str = ""
 
     @field_validator("video_ids", mode="before")
     @classmethod
-    def normalize_video_ids(cls, value: object) -> list[int]:
+    def normalize_video_ids(cls, value: list[int] | None) -> list[int]:
         if value is None:
             return []
         if isinstance(value, list) and all(isinstance(item, int) for item in value):
             return value
         raise ValueError("video_ids must be a list of integer primary keys")
 
     @field_validator("hash_value", mode="before")
     @classmethod
-    def normalize_hash_value(cls, value: object) -> str:
+    def normalize_hash_value(cls, value: str | None) -> str:
         if value is None:
             return ""
         if isinstance(value, str):
             return value
         raise ValueError("hash_value must be a string")
 
 
 class MigrateVideoStreamableStorageCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     video_ids: list[int] = Field(default_factory=list)
     processed_only: bool
     raw_only: bool
     dry_run: bool
 
     @field_validator("video_ids", mode="before")
     @classmethod
-    def normalize_video_ids(cls, value: object) -> list[int]:
+    def normalize_video_ids(cls, value: list[int] | None) -> list[int]:
         if value is None:
             return []
         if isinstance(value, list) and all(isinstance(item, int) for item in value):
             return value
         raise ValueError("video_ids must be a list of integer primary keys")
 
 
 class MigrationMarkEligibleCommandOptionsPayload(BaseModel):
     model_config = ConfigDict(
         extra="ignore", frozen=True, strict=True, populate_by_name=True
     )
 
     apply: bool
     limit: int = Field(default=0, ge=0)
     json_output: bool = Field(validation_alias="json", serialization_alias="json")
 
 
 __all__ = [
     "FrameSegmentReconciliationTrack",
     "ModelInputCommandOptionsPayload",
     "ModelTrainingResultPayload",
     "MigrateDataDirCommandOptionsPayload",
     "MigrateMediaStorageCommandOptionsPayload",
     "MigrateVideoStreamableStorageCommandOptionsPayload",
     "MigrationMarkEligibleCommandOptionsPayload",
     "ReconcileFrameSegmentAnnotationsCommandOptionsPayload",
     "ReconcileMediaIntegrityCommandOptionsPayload",
     "ReconcileSegmentValidationStateCommandOptionsPayload",
     "ReconcileVideoFormatsCommandOptionsPayload",
     "RefreshAuditLedgerIntegrityCommandOptionsPayload",
     "RegisterAiModelCommandOptionsPayload",
     "RegisterAiModelMetaPayload",
     "SetupEndoregDbCommandOptionsPayload",
     "ShowUrlsCommandOptionsPayload",
     "ShowUrlsRoutePayload",
     "ShowUrlsRoutesPayload",
     "StorageManagementCommandOptionsPayload",
     "StorageManagementInfoPayload",
     "TrainImageMultilabelModelCommandOptionsPayload",
     "TrainPhiRegionDetectorCommandOptionsPayload",
     "TranscodeVideoCommandOptionsPayload",
     "TranscodeVideoQualityMode",
     "RuntimeStorageContractPayload",
     "ValidateRuntimeStorageContractCommandOptionsPayload",
     "ValidateVideoFileStatus",
     "ValidateVideoFileStatusPayload",
     "ValidateVideoFilesCommandOptionsPayload",
     "ReapQuarantineCommandOptionsPayload",
     "ReapUploadJobSourcesCommandOptionsPayload",
     "VerboseManagementCommandOptionsPayload",
     "validate_model_training_result",
 ]
diff --git lx_dtypes/models/contracts/media_management.py lx_dtypes/models/contracts/media_management.py
index c9bbc0c..fffdc0e 100644
--- lx_dtypes/models/contracts/media_management.py
+++ lx_dtypes/models/contracts/media_management.py
@@ -1,93 +1,93 @@
 from __future__ import annotations
 
 from typing import Literal
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 MediaManagementCleanupType = Literal["unfinished", "failed", "stale", "all"]
 MediaManagementFileType = Literal["video", "pdf", "all"]
 
 
 class MediaManagementCleanupQueryPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     cleanup_type: MediaManagementCleanupType = "unfinished"
     force: bool = False
     media_type: MediaManagementFileType = "all"
     file_id: int | None = None
 
     @field_validator("file_id", mode="before")
     @classmethod
-    def normalize_file_id(cls, value: object) -> int | None:
+    def normalize_file_id(cls, value: int | str | None) -> int | None:
         if value is None or value == "":
             return None
         if isinstance(value, bool):
             raise ValueError("file_id must be an integer")
         if isinstance(value, int):
             return value
         if isinstance(value, str):
             normalized = value.strip()
             if not normalized:
                 return None
             return int(normalized)
         raise ValueError("file_id must be an integer")
 
 
 class MediaManagementItemPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     id: int = Field(ge=1)
     type: Literal["video", "pdf"]
     filename: str | None = None
     status: str
     uploaded_at: str
     stale_duration_hours: float | None = None
 
 
 class MediaManagementSummaryPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     videos_removed: int = 0
     pdfs_removed: int = 0
     total_removed: int = 0
     stale_videos_removed: int = 0
     dry_run: bool
 
 
 class MediaManagementCleanupResultPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     cleanup_type: MediaManagementCleanupType
     force: bool
     removed_items: list[MediaManagementItemPayload] = Field(default_factory=list)
     summary: MediaManagementSummaryPayload = Field(
         default_factory=lambda: MediaManagementSummaryPayload(dry_run=True)
     )
 
 
 class MediaManagementForceRemoveResponsePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     detail: str
     file_type: Literal["video", "pdf"]
     file_id: int
 
 
 class MediaManagementResetStatusResponsePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     detail: str
     file_type: Literal["video", "pdf"]
     file_id: int
 
 
 __all__ = [
     "MediaManagementCleanupQueryPayload",
     "MediaManagementCleanupResultPayload",
     "MediaManagementCleanupType",
     "MediaManagementFileType",
     "MediaManagementForceRemoveResponsePayload",
     "MediaManagementItemPayload",
     "MediaManagementResetStatusResponsePayload",
     "MediaManagementSummaryPayload",
 ]
diff --git lx_dtypes/models/contracts/model_meta.py lx_dtypes/models/contracts/model_meta.py
index c2401c9..8ed82cd 100644
--- lx_dtypes/models/contracts/model_meta.py
+++ lx_dtypes/models/contracts/model_meta.py
@@ -1,87 +1,124 @@
 from __future__ import annotations
 
 from typing import TypedDict
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 
 class ModelMetaInferenceDatasetConfigData(TypedDict):
     mean: tuple[float, float, float]
     std: tuple[float, float, float]
     size_x: int
     size_y: int
     axes: tuple[int, int, int]
 
 
 class ModelMetaConfigData(TypedDict):
     name: str
     version: str
     model_name: str
     labelset_name: str
     activation: str
     weights_path: str
     mean: str
     std: str
     size_x: int
     size_y: int
     axes: str
     batchsize: int
     num_workers: int
     description: str
 
 
+def _coerce_float(value: object) -> float:
+    if isinstance(value, bool):
+        return float(int(value))
+    if isinstance(value, (int, float)):
+        return float(value)
+    if isinstance(value, str):
+        stripped = value.strip()
+        if not stripped:
+            raise ValueError("value cannot be blank")
+        return float(stripped)
+    raise ValueError("value must be numeric")
+
+
+def _coerce_int(value: object) -> int:
+    if isinstance(value, bool):
+        return int(value)
+    if isinstance(value, int):
+        return int(value)
+    if isinstance(value, float):
+        if not value.is_integer():
+            raise ValueError("value must be an integer")
+        return int(value)
+    if isinstance(value, str):
+        stripped = value.strip()
+        if not stripped:
+            raise ValueError("value cannot be blank")
+        return int(float(stripped)) if "." in stripped else int(stripped)
+    raise ValueError("value must be an integer")
+
 class ModelMetaInferenceDatasetConfigPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     mean: tuple[float, float, float]
     std: tuple[float, float, float]
     size_x: int = Field(ge=1)
     size_y: int = Field(ge=1)
     axes: tuple[int, int, int]
 
     @field_validator("mean", "std", mode="before")
     @classmethod
     def _normalize_float_triplet(
         cls,
-        value: object,
+        value: list[object] | tuple[object, ...],
     ) -> tuple[float, float, float]:
         if isinstance(value, (list, tuple)) and len(value) == 3:
-            return (float(value[0]), float(value[1]), float(value[2]))
+            return (
+                _coerce_float(value[0]),
+                _coerce_float(value[1]),
+                _coerce_float(value[2]),
+            )
         raise ValueError("mean/std must contain exactly three numeric values")
 
     @field_validator("axes", mode="before")
     @classmethod
     def _normalize_axes(
         cls,
-        value: object,
+        value: list[object] | tuple[object, ...],
     ) -> tuple[int, int, int]:
         if isinstance(value, (list, tuple)) and len(value) == 3:
-            return (int(value[0]), int(value[1]), int(value[2]))
+            return (
+                _coerce_int(value[0]),
+                _coerce_int(value[1]),
+                _coerce_int(value[2]),
+            )
         raise ValueError("axes must contain exactly three integer values")
 
 
 class ModelMetaConfigPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str = Field(min_length=1)
     version: str = Field(min_length=1)
     model_name: str = Field(min_length=1)
     labelset_name: str = Field(min_length=1)
     activation: str = Field(min_length=1)
     weights_path: str = ""
     mean: str = Field(min_length=1)
     std: str = Field(min_length=1)
     size_x: int = Field(ge=1)
     size_y: int = Field(ge=1)
     axes: str = Field(min_length=1)
     batchsize: int = Field(ge=1)
     num_workers: int = Field(ge=0)
     description: str = ""
 
 
 __all__ = [
     "ModelMetaConfigData",
     "ModelMetaConfigPayload",
     "ModelMetaInferenceDatasetConfigData",
     "ModelMetaInferenceDatasetConfigPayload",
 ]
diff --git lx_dtypes/models/contracts/patient_examination_report.py lx_dtypes/models/contracts/patient_examination_report.py
index 346860c..916c3de 100644
--- lx_dtypes/models/contracts/patient_examination_report.py
+++ lx_dtypes/models/contracts/patient_examination_report.py
@@ -1,446 +1,456 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from datetime import date, datetime
 from typing import Literal, NotRequired, TypeAlias, TypedDict, cast
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 from .json_types import JsonNull, JsonValue
 
 ReportJsonValue: TypeAlias = JsonValue | JsonNull
 ReportJsonObject: TypeAlias = dict[str, ReportJsonValue]
 ReportStatus: TypeAlias = Literal["draft", "final"]
 SegmentFrameSelectionAction: TypeAlias = Literal["set", "clear", "random", "step"]
 
 
 class PatientReportIdentityData(TypedDict):
     first_name: str
     last_name: str
     dob: date
 
 
 class PatientExaminationReportSubmissionData(TypedDict):
     patient_examination_id: int
     template_name: str
     template_version: str
     template_hash: str
     title: str
     status: ReportStatus
     rendered_text: str
     editor_payload: ReportJsonObject
     patient_data: ReportJsonObject
     indications: list[ReportJsonObject]
     findings: list[ReportJsonObject]
     history_limit: int
     report_id: NotRequired[int]
     expected_version: NotRequired[int]
 
 
 class PatientExaminationReportMakeReportData(TypedDict):
     patient_examination_id: int
     patient: PatientReportIdentityData
     max_frames: int
     report_id: NotRequired[int]
 
 
 class ReportPersistedArtifactsData(TypedDict):
     full_report_id: int | None
     pdf_id: int | None
     pdf_view_url: str | None
     pdf_download_url: str | None
     patient_timeline_url: str | None
 
 
 class ReportSegmentFrameSelectionData(TypedDict, total=False):
     segment_id: int
     video_id: int
     frame_number: int
     frame_id: int | None
     relative_path: str | None
     finding_id: int | None
     patient_finding_id: int | None
     updated_at: str
     selection_source: str
 
 
 ReportSegmentSelectionMap: TypeAlias = dict[str, ReportSegmentFrameSelectionData]
 
 
 class SegmentFramePreviewData(TypedDict):
     frame_id: int
     frame_number: int
     timestamp: float | None
     relative_path: str
     file_exists: bool
     stream_url: str
 
 
 class SegmentFrameControlsData(TypedDict):
     random_frame_number: int
     step_backward_5_frame_number: int
     step_forward_5_frame_number: int
 
 
 class SegmentAttachedFindingData(TypedDict):
     patient_finding_id: int
     finding_id: int | None
     finding_name: str | None
 
 
 class SegmentSelectionMetaData(TypedDict):
     updated_at: str | None
     selection_source: str | None
 
 
 class SegmentFrameSelectorItemData(TypedDict):
     segment_id: int
     video_id: int
     label_id: int | None
     label_name: str | None
     start_frame_number: int
     end_frame_number: int
     segment_duration_seconds: float | None
     selected_frame_number: int | None
     selected_frame: SegmentFramePreviewData | None
     controls: SegmentFrameControlsData
     attached_finding: SegmentAttachedFindingData | None
     selection_meta: SegmentSelectionMetaData
 
 
 class SegmentFrameSelectorResponseData(TypedDict):
     patient_examination_id: int
     report_id: int
     report_status: str
     report_template_name: str
     auto_created_report: bool
     storage_key: str
     count: int
     results: list[SegmentFrameSelectorItemData]
 
 
 class ReportExportFrameDetailData(TypedDict):
     segment_id: int
     video_id: int
     frame_id: int
     frame_number: int
     label_name: str | None
     finding_name: str | None
     stream_url: str
     caption: str
 
 
 class PatientFindingClassificationHistoryData(TypedDict):
     id: int
     classification_id: int | None
     classification_choice_id: int | None
     classification_name: str | None
     classification_choice_name: str | None
-    subcategories: object
-    numerical_descriptors: object
+    subcategories: ReportJsonObject
+    numerical_descriptors: ReportJsonObject
 
 
 class PatientFindingInterventionHistoryData(TypedDict):
     id: int
     intervention_id: int | None
     intervention_name: str | None
-    state: object
-    date: object
-    time_start: object
-    time_end: object
+    state: str | None
+    date: str | None
+    time_start: str | None
+    time_end: str | None
 
 
 class PatientFindingHistoryData(TypedDict):
     patient_finding_id: int
     finding_id: int | None
     finding_name: str | None
     classifications: list[PatientFindingClassificationHistoryData]
     interventions: list[PatientFindingInterventionHistoryData]
 
 
 class PreviousPatientExaminationHistoryData(TypedDict):
     patient_examination_id: int
     examination_id: int | None
     examination_name: str | None
-    date_start: object
-    date_end: object
+    date_start: str | None
+    date_end: str | None
     findings: list[PatientFindingHistoryData]
 
 
 class PatientExaminationHistoryContextData(TypedDict):
     patient_id: int
     patient_examination_id: int
     history_depth: int
     previous_examinations: list[PreviousPatientExaminationHistoryData]
 
 
 class PatientFindingClassificationSyncData(TypedDict, total=False):
-    classification_id: object
-    classification: object
-    classification_choice_id: object
-    classification_choice: object
-    subcategories: object
-    numerical_descriptors: object
+    classification_id: int | None
+    classification: str | None
+    classification_choice_id: int | None
+    classification_choice: str | None
+    subcategories: ReportJsonObject
+    numerical_descriptors: ReportJsonObject
 
 
 class PatientFindingInterventionSyncData(TypedDict, total=False):
-    intervention_id: object
-    intervention: object
-    state: object
-    date: object
-    time_start: object
-    time_end: object
+    intervention_id: int | None
+    intervention: str | None
+    state: str | None
+    date: str | None
+    time_start: str | None
+    time_end: str | None
 
 
 class SegmentFrameSelectorQueryData(TypedDict, total=False):
     patient_examination_id: int
     report_id: int | None
 
 
 class SegmentFrameSelectorPatchData(SegmentFrameSelectorQueryData, total=False):
     segment_id: int
     action: SegmentFrameSelectionAction
     frame_number: int | None
     step: int
     finding_id: int | None
     template_name: str | None
 
 
 class PatientReportIdentityPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     first_name: str = Field(min_length=1, max_length=150)
     last_name: str = Field(min_length=1, max_length=150)
     dob: date
 
 
 class PatientExaminationReportSubmissionPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     report_id: int | None = Field(default=None, ge=1)
     patient_examination_id: int = Field(ge=1)
     template_name: str = Field(min_length=1)
     template_version: str = ""
     template_hash: str = ""
     title: str = ""
     status: ReportStatus = "draft"
     rendered_text: str = ""
     editor_payload: ReportJsonObject = Field(default_factory=dict)
     patient_data: ReportJsonObject = Field(default_factory=dict)
     indications: list[ReportJsonObject] = Field(default_factory=list)
     findings: list[ReportJsonObject] = Field(default_factory=list)
     expected_version: int | None = Field(default=None, ge=1)
     history_limit: int = Field(default=5, ge=1, le=50)
 
 
 class PatientExaminationReportMakeReportPayload(BaseModel):
     model_config = ConfigDict(extra="ignore")
 
     patient_examination_id: int = Field(ge=1)
     report_id: int | None = Field(default=None, ge=1)
     patient: PatientReportIdentityPayload
     max_frames: int = Field(default=12, ge=1, le=24)
 
 
 class ReportPersistedArtifactsPayload(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     full_report_id: int | None = Field(default=None, ge=1)
     pdf_id: int | None = Field(default=None, ge=1)
     pdf_view_url: str | None = None
     pdf_download_url: str | None = None
     patient_timeline_url: str | None = None
 
 
 class ReportSegmentFrameSelectionPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     segment_id: int | None = Field(default=None, ge=1)
     video_id: int | None = Field(default=None, ge=1)
     frame_number: int | None = Field(default=None, ge=0)
     frame_id: int | None = Field(default=None, ge=1)
     relative_path: str | None = None
     finding_id: int | None = Field(default=None, ge=1)
     patient_finding_id: int | None = Field(default=None, ge=1)
     updated_at: str | None = None
     selection_source: str | None = None
 
     @field_validator("relative_path", "updated_at", "selection_source", mode="before")
     @classmethod
-    def blank_to_none(cls, value: object) -> object:
+    def blank_to_none(cls, value: str | int | float | bool | None) -> str | None:
         if isinstance(value, str):
             return value.strip() or None
-        return value
+        if value is None:
+            return None
+        return str(value).strip() or None
 
 
 class SegmentFrameSelectorQueryPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     patient_examination_id: int = Field(ge=1)
     report_id: int | None = Field(default=None, ge=1)
 
     @field_validator("patient_examination_id", "report_id", mode="before")
     @classmethod
-    def normalize_optional_id(cls, value: object) -> object:
+    def normalize_optional_id(cls, value: int | str | None) -> int | str | None:
         if value == "":
             return None
         return value
 
 
 class SegmentFrameSelectorPatchPayload(SegmentFrameSelectorQueryPayload):
     segment_id: int = Field(ge=1)
     action: SegmentFrameSelectionAction = "set"
     frame_number: int | None = Field(default=None, ge=0)
     step: int = 5
     finding_id: int | None = Field(default=None, ge=1)
     template_name: str | None = None
 
     @field_validator("action", mode="before")
     @classmethod
-    def normalize_action(cls, value: object) -> str:
+    def normalize_action(cls, value: str | int | None) -> str:
         return str(value or "set").strip().lower()
 
     @field_validator("frame_number", "finding_id", "template_name", mode="before")
     @classmethod
-    def normalize_blank_optional(cls, value: object) -> object:
+    def normalize_blank_optional(cls, value: int | str | None) -> int | str | None:
         if value == "":
             return None
         if isinstance(value, str) and not value.strip():
             return None
+        if value is None:
+            return None
         return value
 
 
-def report_json_safe(value: object) -> ReportJsonValue:
+def report_json_safe(
+    value: object,
+) -> ReportJsonValue:
     if value is None or isinstance(value, (str, int, float, bool)):
         return cast(ReportJsonValue, value)
     if isinstance(value, Mapping):
-        mapping = cast(Mapping[object, object], value)
+        mapping = cast(Mapping[str, object], value)
         return cast(
             ReportJsonValue,
             {str(key): report_json_safe(item) for key, item in mapping.items()},
         )
     if isinstance(value, (list, tuple)):
-        items = cast(list[object] | tuple[object, ...], value)
-        return cast(ReportJsonValue, [report_json_safe(item) for item in items])
+        return cast(ReportJsonValue, [report_json_safe(item) for item in value])
     if isinstance(value, (date, datetime)):
         return cast(ReportJsonValue, value.isoformat())
     return cast(ReportJsonValue, str(value))
 
 
-def report_json_safe_dict(payload: object) -> ReportJsonObject:
+def report_json_safe_dict(payload: Mapping[str, JsonValue]) -> ReportJsonObject:
     if not isinstance(payload, Mapping):
         return {}
     mapping = cast(Mapping[object, object], payload)
-    return {str(key): report_json_safe(value) for key, value in mapping.items()}
+    return cast(
+        ReportJsonObject,
+        {str(key): report_json_safe(value) for key, value in mapping.items()},
+    )
 
 
 def dump_report_submission_payload(
     payload: PatientExaminationReportSubmissionPayload,
 ) -> PatientExaminationReportSubmissionData:
     return cast(
         PatientExaminationReportSubmissionData,
         payload.model_dump(mode="python", exclude_none=True),
     )
 
 
 def dump_make_report_payload(
     payload: PatientExaminationReportMakeReportPayload,
 ) -> PatientExaminationReportMakeReportData:
     return cast(
         PatientExaminationReportMakeReportData,
         payload.model_dump(mode="python", exclude_none=True),
     )
 
 
 def dump_persisted_artifacts_payload(
     payload: ReportPersistedArtifactsPayload,
 ) -> ReportPersistedArtifactsData:
     return cast(ReportPersistedArtifactsData, payload.model_dump(mode="json"))
 
 
 def dump_segment_frame_selection_payload(
     payload: ReportSegmentFrameSelectionPayload,
 ) -> ReportSegmentFrameSelectionData:
     return cast(
         ReportSegmentFrameSelectionData,
         payload.model_dump(mode="json", exclude_none=True),
     )
 
 
-def validate_segment_selection_map(payload: object) -> ReportSegmentSelectionMap:
+def validate_segment_selection_map(
+    payload: object
+) -> ReportSegmentSelectionMap:
     if not isinstance(payload, Mapping):
         raise ValueError("segment selection map must be a JSON object")
     result: ReportSegmentSelectionMap = {}
     selection_map = cast(Mapping[object, object], payload)
     for key, value in selection_map.items():
         if not isinstance(value, Mapping):
             raise ValueError(f"segment selection entry {key!s} must be a JSON object")
-        selection = cast(Mapping[str, object], value)
+        selection = cast(Mapping[str, JsonValue], value)
         result[str(key)] = dump_segment_frame_selection_payload(
             ReportSegmentFrameSelectionPayload.model_validate(dict(selection))
         )
     return result
 
 
 def dump_selector_query_payload(
     payload: SegmentFrameSelectorQueryPayload,
 ) -> SegmentFrameSelectorQueryData:
     return cast(
         SegmentFrameSelectorQueryData,
         payload.model_dump(mode="python", exclude_none=True),
     )
 
 
 def dump_selector_patch_payload(
     payload: SegmentFrameSelectorPatchPayload,
 ) -> SegmentFrameSelectorPatchData:
     return cast(
         SegmentFrameSelectorPatchData,
         payload.model_dump(mode="python", exclude_none=True),
     )
 
 
 __all__ = [
     "PatientExaminationReportMakeReportData",
     "PreviousPatientExaminationHistoryData",
     "PatientFindingInterventionSyncData",
     "PatientFindingInterventionHistoryData",
     "PatientFindingHistoryData",
     "PatientFindingClassificationSyncData",
     "PatientFindingClassificationHistoryData",
     "PatientExaminationHistoryContextData",
     "PatientExaminationReportMakeReportPayload",
     "PatientExaminationReportSubmissionData",
     "PatientExaminationReportSubmissionPayload",
     "PatientReportIdentityData",
     "PatientReportIdentityPayload",
     "ReportExportFrameDetailData",
     "ReportJsonObject",
     "ReportJsonValue",
     "ReportPersistedArtifactsData",
     "ReportPersistedArtifactsPayload",
     "ReportSegmentFrameSelectionData",
     "ReportSegmentFrameSelectionPayload",
     "ReportSegmentSelectionMap",
     "ReportStatus",
     "SegmentAttachedFindingData",
     "SegmentFrameControlsData",
     "SegmentFramePreviewData",
     "SegmentFrameSelectionAction",
     "SegmentFrameSelectorItemData",
     "SegmentFrameSelectorPatchData",
     "SegmentFrameSelectorPatchPayload",
     "SegmentFrameSelectorQueryData",
     "SegmentFrameSelectorQueryPayload",
     "SegmentFrameSelectorResponseData",
     "SegmentSelectionMetaData",
     "dump_make_report_payload",
     "dump_persisted_artifacts_payload",
     "dump_report_submission_payload",
     "dump_segment_frame_selection_payload",
     "dump_selector_patch_payload",
     "dump_selector_query_payload",
     "report_json_safe",
     "report_json_safe_dict",
     "validate_segment_selection_map",
 ]
diff --git lx_dtypes/models/contracts/patient_finding_classification_runtime.py lx_dtypes/models/contracts/patient_finding_classification_runtime.py
index 8bdd568..d0ab3b1 100644
--- lx_dtypes/models/contracts/patient_finding_classification_runtime.py
+++ lx_dtypes/models/contracts/patient_finding_classification_runtime.py
@@ -1,112 +1,113 @@
 from __future__ import annotations
 
 from math import isfinite
 from typing import Literal, TypeAlias
 
 from pydantic import (
     BaseModel,
     ConfigDict,
     Field,
     RootModel,
     field_validator,
     model_validator,
 )
+from lx_dtypes.models.contracts.json_types import JsonObject
 
 
 class PatientFindingClassificationSubcategoryPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     required: bool = False
     choices: list[str] = Field(min_length=1)
     value: str | None = None
 
     @field_validator("choices")
     @classmethod
     def validate_choices(cls, value: list[str]) -> list[str]:
         normalized: list[str] = []
         seen: set[str] = set()
         for choice in value:
             stripped = choice.strip()
             if not stripped:
                 raise ValueError("choices must not contain blank values")
             if stripped in seen:
                 raise ValueError("choices must not contain duplicate values")
             seen.add(stripped)
             normalized.append(stripped)
         return normalized
 
     @field_validator("value")
     @classmethod
     def validate_value_text(cls, value: str | None) -> str | None:
         if value is None:
             return None
         stripped = value.strip()
         if not stripped:
             raise ValueError("value must not be blank")
         return stripped
 
     @model_validator(mode="after")
     def validate_selected_value(
         self,
     ) -> PatientFindingClassificationSubcategoryPayload:
         if self.value is not None and self.value not in self.choices:
             raise ValueError("value must be one of choices")
         return self
 
 
 class PatientFindingClassificationNumericalDescriptorPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     min: float = 0.0
     max: float = 1.0
     distribution: Literal["normal", "uniform"] = "normal"
     mean: float = 0.5
     std: float = Field(default=0.1, ge=0)
     value: float | None = None
 
     @field_validator("min", "max", "mean", "std", "value")
     @classmethod
     def validate_finite_number(cls, value: float | None) -> float | None:
         if value is not None and not isfinite(value):
             raise ValueError("numeric descriptor values must be finite")
         return value
 
     @model_validator(mode="after")
     def validate_numeric_bounds(
         self,
     ) -> PatientFindingClassificationNumericalDescriptorPayload:
         if self.min > self.max:
             raise ValueError("min must not exceed max")
         if self.mean < self.min or self.mean > self.max:
             raise ValueError("mean must be within min and max")
         if self.value is not None and (self.value < self.min or self.value > self.max):
             raise ValueError("value must be within min and max")
         return self
 
 
 class PatientFindingClassificationSubcategoriesPayload(
     RootModel[dict[str, PatientFindingClassificationSubcategoryPayload]]
 ):
     model_config = ConfigDict(frozen=True, strict=True)
 
 
 class PatientFindingClassificationNumericalDescriptorsPayload(
     RootModel[dict[str, PatientFindingClassificationNumericalDescriptorPayload]]
 ):
     model_config = ConfigDict(frozen=True, strict=True)
 
 
-PatientFindingClassificationSubcategoriesData: TypeAlias = dict[str, dict[str, object]]
+PatientFindingClassificationSubcategoriesData: TypeAlias = dict[str, JsonObject]
 PatientFindingClassificationNumericalDescriptorsData: TypeAlias = dict[
-    str, dict[str, object]
+    str, JsonObject
 ]
 
 
 __all__ = [
     "PatientFindingClassificationNumericalDescriptorPayload",
     "PatientFindingClassificationNumericalDescriptorsData",
     "PatientFindingClassificationNumericalDescriptorsPayload",
     "PatientFindingClassificationSubcategoryPayload",
     "PatientFindingClassificationSubcategoriesData",
     "PatientFindingClassificationSubcategoriesPayload",
 ]
diff --git lx_dtypes/models/contracts/pdf_redaction.py lx_dtypes/models/contracts/pdf_redaction.py
index 3aad111..2962c77 100644
--- lx_dtypes/models/contracts/pdf_redaction.py
+++ lx_dtypes/models/contracts/pdf_redaction.py
@@ -1,134 +1,140 @@
 from __future__ import annotations
 
 import json
-from typing import Any, Literal
+from typing import Literal, cast
+
+from collections.abc import Mapping
 
 from pydantic import BaseModel, ConfigDict, field_validator
 
+from .json_types import JsonValue
+
 
 class PdfRedactionBox(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     x: float
     y: float
     width: float
     height: float
 
     @field_validator("x", "y", "width", "height")
     @classmethod
     def validate_normalized_coordinate(cls, value: float) -> float:
         if value < 0 or value > 1:
             raise ValueError("value must be within [0, 1]")
         return value
 
     @field_validator("height", "width")
     @classmethod
     def validate_positive_size(cls, value: float) -> float:
         if value <= 0:
             raise ValueError("value must be > 0")
         return value
 
 
 class PdfRedactionPage(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     page: int
     boxes: list[PdfRedactionBox]
 
     @field_validator("page")
     @classmethod
     def validate_page(cls, value: int) -> int:
         if value < 1:
             raise ValueError("page must be an integer >= 1")
         return value
 
 
 class PdfRedactionManifest(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     version: int
     normalized: Literal[True]
     pages: list[PdfRedactionPage]
 
     @field_validator("version")
     @classmethod
     def validate_version(cls, value: int) -> int:
         if value < 1:
             raise ValueError("version must be an integer >= 1")
         return value
 
     @field_validator("pages")
     @classmethod
     def validate_pages(cls, value: list[PdfRedactionPage]) -> list[PdfRedactionPage]:
         for page_entry in value:
             for box in page_entry.boxes:
                 if box.x + box.width > 1 or box.y + box.height > 1:
                     raise ValueError("all boxes must fit inside normalized page bounds")
         return value
 
 
 class PdfRedactionRequest(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     source_type: Literal["raw", "processed"]
     redaction_manifest: PdfRedactionManifest
     note: str = ""
     client_source_sha256: str = ""
 
     @field_validator("redaction_manifest", mode="before")
     @classmethod
-    def parse_manifest(cls, value: object) -> object:
+    def parse_manifest(cls, value: str | Mapping[str, JsonValue]) -> dict[str, JsonValue]:
         if isinstance(value, str):
             payload = value.strip()
             if not payload:
                 raise ValueError("redaction_manifest must not be empty")
             parsed = json.loads(payload)
             if not isinstance(parsed, dict):
                 raise ValueError("redaction_manifest must be a JSON object")
-            return parsed
-        return value
+            return cast(dict[str, JsonValue], parsed)
+        return cast(dict[str, JsonValue], value)
 
     @field_validator("note", mode="before")
     @classmethod
-    def normalize_note(cls, value: Any) -> str:
+    def normalize_note(cls, value: str | int | float | bool | None) -> str:
         if value in (None, ""):
             return ""
         return str(value).strip()
 
     @field_validator("client_source_sha256", mode="before")
     @classmethod
-    def normalize_client_source_sha256(cls, value: Any) -> str:
+    def normalize_client_source_sha256(
+        cls, value: str | int | float | bool | None
+    ) -> str:
         if value in (None, ""):
             return ""
         return str(value).strip().lower()
 
     @field_validator("client_source_sha256")
     @classmethod
     def validate_client_source_sha256(cls, value: str) -> str:
         if not value:
             return value
         if len(value) != 64:
             raise ValueError("client_source_sha256 must contain 64 hex chars")
         if any(ch not in "0123456789abcdef" for ch in value):
             raise ValueError("client_source_sha256 must be lowercase hex")
         return value
 
 
 class PdfRedactionResponse(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     file_id: int
     revision_id: int
     processed_stream_url: str
     status: str
     anonymization_validated: bool
     updated_at: str
 
 
 __all__ = [
     "PdfRedactionBox",
     "PdfRedactionManifest",
     "PdfRedactionPage",
     "PdfRedactionRequest",
     "PdfRedactionResponse",
 ]
diff --git lx_dtypes/models/contracts/report_anonymization.py lx_dtypes/models/contracts/report_anonymization.py
index c3a414d..6483ae5 100644
--- lx_dtypes/models/contracts/report_anonymization.py
+++ lx_dtypes/models/contracts/report_anonymization.py
@@ -1,251 +1,250 @@
 from __future__ import annotations
 
 from collections.abc import Sequence
 from enum import StrEnum
 from pathlib import Path
 from typing import Literal
 from uuid import UUID
 
 from lx_dtypes.models.meta.SensitiveMeta import SensitiveMeta
 from pydantic import (
     BaseModel,
     ConfigDict,
     Field,
     field_validator,
     model_validator,
 )
 
 REPORT_ANONYMIZATION_CONTRACT_VERSION = "report_anonymization_v2"
 ReportAnonymizationContractVersion = Literal["report_anonymization_v2"]
 
 
 class ReportAnonymizationPhase(StrEnum):
     VALIDATE_REQUEST = "validate_request"
     EXTRACT_TEXT = "extract_text"
     EXTRACT_METADATA = "extract_metadata"
     ANONYMIZE_TEXT = "anonymize_text"
     WRITE_ARTIFACT = "write_artifact"
     VALIDATE_ARTIFACT = "validate_artifact"
 
 
 class ReportAnonymizationWarningCode(StrEnum):
     NONDETERMINISTIC_PROVIDER = "nondeterministic_provider"
     PDF_REPAIRED = "pdf_repaired"
 
 
 class ReportAnonymizationErrorCode(StrEnum):
     INVALID_CONTRACT = "invalid_contract"
     SOURCE_IDENTITY_MISMATCH = "source_identity_mismatch"
     UNSUPPORTED_DOCUMENT = "unsupported_document"
     RESOURCE_LIMIT = "resource_limit"
     PROVIDER_UNAVAILABLE = "provider_unavailable"
     VALIDATION_FAILED = "validation_failed"
     ARTIFACT_EXISTS = "artifact_exists"
     DEADLINE_EXCEEDED = "deadline_exceeded"
     CANCELLED = "cancelled"
 
 
 class ReportAnonymizationOptions(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     use_ensemble: bool = False
     verbose: bool = True
     use_llm: bool | None = None
 
 
 class ReportAnonymizationRequestV2(BaseModel):
     """One immutable local report snapshot assigned to one host-owned attempt."""
 
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     contract_version: ReportAnonymizationContractVersion = (
         REPORT_ANONYMIZATION_CONTRACT_VERSION
     )
     attempt_id: UUID
     source_path: Path
     source_sha256: str = Field(pattern=r"^[0-9a-f]{64}$")
     source_size_bytes: int = Field(gt=0)
     output_directory: Path
     create_anonymized_pdf: Literal[True] = True
     deadline_monotonic_ns: int | None = Field(default=None, gt=0)
     options: ReportAnonymizationOptions = Field(
         default_factory=ReportAnonymizationOptions
     )
 
     @model_validator(mode="after")
     def validate_local_paths(self) -> "ReportAnonymizationRequestV2":
         if self.source_path.is_symlink() or not self.source_path.is_file():
             raise ValueError("source_path must be a regular non-symlink file")
         if self.output_directory.is_symlink() or not self.output_directory.is_dir():
             raise ValueError(
                 "output_directory must be an existing non-symlink directory"
             )
         if self.source_path.resolve() == self.output_directory.resolve():
             raise ValueError("source_path and output_directory must be different")
         return self
 
 
 class ReportAnonymizationProvenanceV2(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     contract_version: ReportAnonymizationContractVersion = (
         REPORT_ANONYMIZATION_CONTRACT_VERSION
     )
     implementation: Literal["lx_anonymizer.ReportReader"] = "lx_anonymizer.ReportReader"
     anonymizer_version: str = Field(min_length=1)
     detector_sources: tuple[str, ...] = ()
     model_names: tuple[str, ...] = ()
     model_versions: dict[str, str] = Field(default_factory=dict)
     proposal_counts: dict[str, int] = Field(default_factory=dict)
     used_llm: bool
     deterministic: bool
 
     @field_validator("anonymizer_version")
     @classmethod
     def validate_anonymizer_version(cls, value: str) -> str:
         if not value.strip():
             raise ValueError("anonymizer_version must not be blank")
         return value
 
     @field_validator("detector_sources", "model_names")
     @classmethod
     def validate_names(cls, value: tuple[str, ...]) -> tuple[str, ...]:
         if any(not item.strip() for item in value):
             raise ValueError("provenance names must not contain blank values")
         return value
 
     @field_validator("model_versions")
     @classmethod
     def validate_model_versions(cls, value: dict[str, str]) -> dict[str, str]:
         if any(not key.strip() or not item.strip() for key, item in value.items()):
             raise ValueError("model_versions keys and values must not be blank")
         return value
 
     @field_validator("proposal_counts")
     @classmethod
     def validate_proposal_counts(cls, value: dict[str, int]) -> dict[str, int]:
         if any(not key.strip() or count < 0 for key, count in value.items()):
             raise ValueError(
                 "proposal_counts requires non-blank keys and non-negative counts"
             )
         return value
 
 
 class ReportArtifactValidationV2(BaseModel):
     """Evidence that the closed attempt artifact passed a full parser traversal."""
 
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     validator: Literal["pymupdf_full_parse_v1"] = "pymupdf_full_parse_v1"
     page_count: int = Field(gt=0)
     encrypted: Literal[False] = False
     repaired: bool
 
 
 class ReportAnonymizationWarningV2(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     code: ReportAnonymizationWarningCode
     phase: ReportAnonymizationPhase
 
 
 class ReportAnonymizationResultV2(BaseModel):
     model_config = ConfigDict(
         arbitrary_types_allowed=True,
         extra="forbid",
         frozen=True,
         strict=True,
         str_strip_whitespace=False,
     )
 
     contract_version: ReportAnonymizationContractVersion = (
         REPORT_ANONYMIZATION_CONTRACT_VERSION
     )
     attempt_id: UUID
     source_sha256: str = Field(pattern=r"^[0-9a-f]{64}$")
     original_text: str
     anonymized_text: str
     extracted_metadata: SensitiveMeta
     artifact_path: Path
     artifact_sha256: str = Field(pattern=r"^[0-9a-f]{64}$")
     artifact_size_bytes: int = Field(gt=0)
     artifact_validation: ReportArtifactValidationV2
     provenance: ReportAnonymizationProvenanceV2
     warnings: tuple[ReportAnonymizationWarningV2, ...] = ()
 
 
 class ReportAnonymizationFailureV2(BaseModel):
     """Machine-safe failure classification returned across integration boundaries."""
 
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     contract_version: ReportAnonymizationContractVersion = (
         REPORT_ANONYMIZATION_CONTRACT_VERSION
     )
     attempt_id: UUID
     phase: ReportAnonymizationPhase
     error_code: ReportAnonymizationErrorCode
     retryable: bool
 
 
 class ReportAnonymizationResult(BaseModel):
     model_config = ConfigDict(
         arbitrary_types_allowed=True,
         extra="forbid",
         frozen=True,
         strict=True,
         str_strip_whitespace=False,
     )
 
     original_text: str
     anonymized_text: str
     extracted_metadata: SensitiveMeta = Field(default_factory=SensitiveMeta)
     anonymized_path: Path
 
     @field_validator("original_text", "anonymized_text", mode="before")
     @classmethod
-    def normalize_text(cls, value: object) -> str:
+    def normalize_text(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         return str(value)
 
     @field_validator("extracted_metadata", mode="before")
     @classmethod
-    def normalize_extracted_metadata(cls, value: object) -> SensitiveMeta:
+    def normalize_extracted_metadata(cls, value: SensitiveMeta | dict | None) -> SensitiveMeta:
         if isinstance(value, SensitiveMeta):
             return value
         return SensitiveMeta.from_dict(value if isinstance(value, dict) else None)
 
     @classmethod
     def from_process_report_result(
-        cls,
-        value: Sequence[object],
+        cls, value: Sequence[str | Path | dict | None]
     ) -> "ReportAnonymizationResult":
         if len(value) != 4:
             raise ValueError("process_report result must contain exactly four values")
         original_text, anonymized_text, extracted_metadata, anonymized_path = value
         return cls.model_validate(
             {
                 "original_text": original_text,
                 "anonymized_text": anonymized_text,
                 "extracted_metadata": extracted_metadata,
                 "anonymized_path": anonymized_path,
             }
         )
 
 
 __all__ = [
     "REPORT_ANONYMIZATION_CONTRACT_VERSION",
     "ReportAnonymizationContractVersion",
     "ReportAnonymizationErrorCode",
     "ReportAnonymizationFailureV2",
     "ReportAnonymizationOptions",
     "ReportAnonymizationPhase",
     "ReportAnonymizationProvenanceV2",
     "ReportAnonymizationRequestV2",
     "ReportAnonymizationResult",
     "ReportAnonymizationResultV2",
     "ReportAnonymizationWarningCode",
     "ReportAnonymizationWarningV2",
     "ReportArtifactValidationV2",
 ]
diff --git lx_dtypes/models/contracts/report_context.py lx_dtypes/models/contracts/report_context.py
index 4dec15c..22e10de 100644
--- lx_dtypes/models/contracts/report_context.py
+++ lx_dtypes/models/contracts/report_context.py
@@ -1,30 +1,30 @@
 from __future__ import annotations
 
 from pydantic import BaseModel, ConfigDict, field_validator
 
 from .document_type import DocumentType
 
 
 class ReportContext(BaseModel):
     patient_examination_id: int
     patient_id: int
     document_type: DocumentType
     anonymized_text: str
     report_template_name: str | None = None
     report_template_version: str | None = None
     language: str | None = None
     examination_hash: str | None = None
     patient_hash: str | None = None
     source_pdf_id: int | None = None
 
     model_config = ConfigDict(extra="forbid")
 
     @field_validator("anonymized_text", mode="before")
     @classmethod
-    def normalize_text(cls, value: object) -> str:
+    def normalize_text(cls, value: str | int | float | bool | None) -> str:
         if value is None:
             return ""
         return str(value)
 
 
 __all__ = ["ReportContext"]
diff --git lx_dtypes/models/contracts/setup_config.py lx_dtypes/models/contracts/setup_config.py
index 38c9bce..4a47635 100644
--- lx_dtypes/models/contracts/setup_config.py
+++ lx_dtypes/models/contracts/setup_config.py
@@ -1,73 +1,74 @@
 from __future__ import annotations
 
 
 from pydantic import BaseModel, ConfigDict, Field
+from lx_dtypes.models.contracts.json_types import JsonObject
 
 
 class SetupConfigDefaultModelsPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     primary_classification_model: str = Field(min_length=1)
     primary_labelset: str = Field(min_length=1)
 
 
 class SetupConfigHuggingFaceFallbackPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     enabled: bool
     repo_id: str = Field(min_length=1)
     filename: str = Field(min_length=1)
     labelset_name: str = Field(min_length=1)
 
 
 class SetupConfigAutoGenerationDefaultsPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     activation: str = Field(min_length=1)
     mean: str = Field(min_length=1)
     std: str = Field(min_length=1)
     size_x: int
     size_y: int
     axes: str = Field(min_length=1)
     batchsize: int
     num_workers: int
 
 
 class SetupConfigDataPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     default_models: SetupConfigDefaultModelsPayload
     huggingface_fallback: SetupConfigHuggingFaceFallbackPayload
     weights_search_patterns: list[str]
     weights_search_dirs: list[str]
     auto_generation_defaults: SetupConfigAutoGenerationDefaultsPayload
 
 
 class SetupConfigModelSpecificDataPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
-    setup_config: dict[str, object] = Field(default_factory=dict)
+    setup_config: JsonObject = Field(default_factory=dict)
 
 
 class SetupConfigModelSpecificEntryFieldsPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     name: str | None = None
     model: str | None = None
 
 
 class SetupConfigModelSpecificEntryPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, strict=True)
 
     fields: SetupConfigModelSpecificEntryFieldsPayload
 
 
 __all__ = [
     "SetupConfigAutoGenerationDefaultsPayload",
     "SetupConfigDataPayload",
     "SetupConfigDefaultModelsPayload",
     "SetupConfigHuggingFaceFallbackPayload",
     "SetupConfigModelSpecificDataPayload",
     "SetupConfigModelSpecificEntryFieldsPayload",
     "SetupConfigModelSpecificEntryPayload",
 ]
diff --git lx_dtypes/models/contracts/text_anonymization.py lx_dtypes/models/contracts/text_anonymization.py
index 78d0bad..367ce6c 100644
--- lx_dtypes/models/contracts/text_anonymization.py
+++ lx_dtypes/models/contracts/text_anonymization.py
@@ -1,118 +1,118 @@
 from __future__ import annotations
 
 from enum import Enum
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 
 class TextAnonymizationMeta(BaseModel):
     model_config = ConfigDict(extra="ignore", strict=True)
 
     pdf_hash: str = ""
     file_path: str = ""
     first_name: str = ""
     last_name: str = ""
     examiner_first_name: str = ""
     examiner_last_name: str = ""
     casenumber: str = ""
     examination_date: str = ""
     dob: str = ""
 
     @field_validator(
         "pdf_hash",
         "file_path",
         "first_name",
         "last_name",
         "examiner_first_name",
         "examiner_last_name",
         "casenumber",
         "examination_date",
         "dob",
         mode="before",
     )
     @classmethod
-    def normalize_text(cls, value: object) -> str:
+    def normalize_text(cls, value: str | int | None) -> str:
         if value is None:
             return ""
         return str(value).strip()
 
 
 class GenderGuess(str, Enum):
     MALE = "male"
     MOSTLY_MALE = "mostly_male"
     FEMALE = "female"
     MOSTLY_FEMALE = "mostly_female"
     UNKNOWN = "unknown"
     ANDY = "andy"
 
 
 class GenderDisplayLabel(str, Enum):
     MALE = "Male"
     FEMALE = "Female"
     NEUTRAL = "Neutral"
 
 
 class DateOfBirthCore(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True)
 
     day: int = Field(ge=1, le=31)
     month: int = Field(ge=1, le=12)
     year: int = Field(ge=1900, le=2100)
 
 
 class PersonNameMetadata(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True)
 
     first_name: str
     last_name: str
     dob: DateOfBirthCore
     gender_label: GenderDisplayLabel
 
     @field_validator("first_name", "last_name", mode="before")
     @classmethod
-    def normalize_name(cls, value: object) -> str:
+    def normalize_name(cls, value: str | int | float | bool | None) -> str:
         text = str(value).strip()
         if not text:
             raise ValueError("name components must not be empty")
         return text
 
 
 class LLMMetadataPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", strict=True)
 
     first_name: str = ""
     last_name: str = ""
     gender: str = ""
     dob: str = ""
     casenumber: str = ""
     examination_date: str = ""
     examination_time: str = ""
     examiner_first_name: str = ""
     examiner_last_name: str = ""
 
     @field_validator(
         "first_name",
         "last_name",
         "gender",
         "dob",
         "casenumber",
         "examination_date",
         "examination_time",
         "examiner_first_name",
         "examiner_last_name",
         mode="before",
     )
     @classmethod
-    def normalize_text_fields(cls, value: object) -> str:
+    def normalize_text_fields(cls, value: str | int | None) -> str:
         if value is None:
             return ""
         return str(value).strip()
 
 
 __all__ = [
     "TextAnonymizationMeta",
     "GenderGuess",
     "GenderDisplayLabel",
     "DateOfBirthCore",
     "PersonNameMetadata",
     "LLMMetadataPayload",
 ]
diff --git lx_dtypes/models/contracts/validated_identity.py lx_dtypes/models/contracts/validated_identity.py
index 95cb004..c06c33f 100644
--- lx_dtypes/models/contracts/validated_identity.py
+++ lx_dtypes/models/contracts/validated_identity.py
@@ -1,32 +1,33 @@
 from __future__ import annotations
 
 from typing import Literal
 
 from pydantic import BaseModel, ConfigDict, Field
+from lx_dtypes.models.contracts.json_types import JsonObject
 
 
 MediaType = Literal["video", "pdf"]
 
 
 class ValidatedIdentityPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True, frozen=True)
 
     source: str = Field(min_length=1)
     media_type: MediaType
     media_pk: str = Field(min_length=1)
     sensitive_meta_id: int = Field(ge=1)
     patient_hash: str | None = None
     examination_hash: str | None = None
     pseudo_patient_id: int | None = None
     pseudo_examination_id: int | None = None
     linked_patient_id: int | None = None
     linked_patient_examination_id: int | None = None
     case_resolution_status: str
     case_resolution_reason: str | None = None
     case_resolution_created: bool
 
 
 def dump_validated_identity_payload(
     payload: ValidatedIdentityPayload,
-) -> dict[str, object]:
+) -> JsonObject:
     return payload.model_dump(mode="python")
diff --git lx_dtypes/models/contracts/video_ai_labels.py lx_dtypes/models/contracts/video_ai_labels.py
index e87586f..48d20fb 100644
--- lx_dtypes/models/contracts/video_ai_labels.py
+++ lx_dtypes/models/contracts/video_ai_labels.py
@@ -1,279 +1,279 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from typing import TypeAlias, cast
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 from .json_types import JsonObject, JsonValue
 
 VideoAiJsonObject: TypeAlias = JsonObject
 
 
 def _empty_label_list() -> list[VideoAiLabelPayload]:
     return []
 
 
 def _empty_model_meta_list() -> list[VideoAiPredictionModelMetaPayload]:
     return []
 
 
 def _empty_huggingface_model_list() -> list[VideoAiHuggingFaceModelPayload]:
     return []
 
 
-def _strip_optional_text(value: object) -> object:
+def _strip_optional_text(value: str | int | float | bool | None) -> str | None:
     if value is None:
         return None
     if isinstance(value, str):
         return value.strip() or None
     return str(value).strip() or None
 
 
 class VideoAiLabelPayload(BaseModel):
     """A minimal label reference returned by video AI endpoints."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     id: int = Field(ge=1)
     name: str = Field(min_length=1)
 
 
 class VideoAiLabelSetPayload(BaseModel):
     """Label-set metadata returned by the video AI label-set list endpoint."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     id: int = Field(ge=1)
     name: str = Field(min_length=1)
     version: int
     description: str = ""
     label_count: int = Field(ge=0)
     labels: list[VideoAiLabelPayload] = Field(default_factory=_empty_label_list)
 
 
 class VideoAiPredictionModelMetaPayload(BaseModel):
     """Locally registered prediction model metadata for video AI endpoints."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     id: int = Field(ge=1)
     name: str = Field(min_length=1)
     version: str = Field(min_length=1)
     description: str = ""
     model_name: str = Field(min_length=1)
     ai_model_id: int = Field(ge=1)
     labelset_name: str = Field(min_length=1)
     labelset_version: int
     labelset_id: int = Field(ge=1)
     weights_available: bool
     is_active: bool
 
 
 class VideoAiHuggingFaceModelPayload(BaseModel):
     """Known Hugging Face model option exposed by the prediction model list."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     model_id: str = Field(min_length=1)
     label: str = Field(min_length=1)
     labelset_name: str = Field(min_length=1)
 
 
 class VideoAiPredictionModelListPayload(BaseModel):
     """Response payload for locally registered and materializable model choices."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     models: list[VideoAiPredictionModelMetaPayload] = Field(
         default_factory=_empty_model_meta_list
     )
     default_huggingface_model_id: str = Field(min_length=1)
     default_model_name: str = Field(min_length=1)
     default_labelset_name: str = Field(min_length=1)
     huggingface_models: list[VideoAiHuggingFaceModelPayload] = Field(
         default_factory=_empty_huggingface_model_list
     )
 
 
 class VideoAiRerunPredictionRequestPayload(BaseModel):
     """Validated request payload for rerunning video prediction segments."""
 
     model_config = ConfigDict(
         extra="allow",
         populate_by_name=True,
         str_strip_whitespace=True,
     )
 
     model_meta_id: int | None = Field(default=None, ge=1)
     hf_model_id: str | None = None
     huggingface_model_id: str | None = None
     model_id: str | None = None
     labelset_name: str | None = None
     label_set_name: str | None = None
     labelset_version: int | str | None = None
     model_name: str | None = None
     model_meta_version: str | None = None
     replace_prediction_segments: bool = True
     delete_frames_after: bool = True
     ocr_frame_fraction: float = Field(default=0.001, ge=0)
     ocr_cap: int = Field(default=10, ge=0)
     test_run: bool = False
     n_test_frames: int = Field(default=10, ge=1)
 
     @field_validator(
         "hf_model_id",
         "huggingface_model_id",
         "model_id",
         "labelset_name",
         "label_set_name",
         "labelset_version",
         "model_name",
         "model_meta_version",
         mode="before",
     )
     @classmethod
-    def _normalize_optional_text(cls, value: object) -> object:
+    def _normalize_optional_text(cls, value: str | int | float | bool | None) -> str | None:
         return _strip_optional_text(value)
 
     @field_validator(
         "replace_prediction_segments",
         "delete_frames_after",
         "test_run",
         mode="before",
     )
     @classmethod
-    def _normalize_bool(cls, value: object) -> object:
+    def _normalize_bool(cls, value: bool | str | int | None) -> bool | str | int | None:
         if value is None or isinstance(value, bool):
             return value
         if isinstance(value, str):
             normalized = value.strip().lower()
             if normalized in {"1", "true", "yes", "y", "on"}:
                 return True
             if normalized in {"0", "false", "no", "n", "off"}:
                 return False
         return value
 
     @property
     def resolved_huggingface_model_id(self) -> str | None:
         return self.hf_model_id or self.huggingface_model_id or self.model_id
 
     @property
     def resolved_labelset_name(self) -> str | None:
         return self.labelset_name or self.label_set_name
 
     def to_temporal_options_payload(self) -> VideoAiJsonObject:
         return cast(
             VideoAiJsonObject, self.model_dump(mode="python", exclude_none=True)
         )
 
 
 class VideoAiPredictionJobPayload(BaseModel):
     """Job metadata returned after dispatching prediction reruns."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     task_id: str
     history_id: int | None = Field(default=None, ge=1)
     mode: str
     queue: str
 
 
 class VideoAiRerunPredictionResponsePayload(BaseModel):
     """Response payload for the video prediction rerun endpoint."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     success: bool
     status: str = Field(min_length=1)
     queued: bool
     pending: bool
     video_id: int = Field(ge=1)
     model_meta: VideoAiPredictionModelMetaPayload
     job: VideoAiPredictionJobPayload
     deleted_prediction_segments: int | None = Field(default=None, ge=0)
     prediction_segments_count: int = Field(ge=0)
     reason: str | None = None
     message: str | None = None
     blocked_by_history_id: int | None = Field(default=None, ge=1)
 
     def to_response_dict(self) -> VideoAiJsonObject:
         payload = self.model_dump(mode="json", exclude_none=True)
         payload["job"] = self.job.model_dump(mode="json", exclude_none=False)
         payload["deleted_prediction_segments"] = self.deleted_prediction_segments
         return cast(VideoAiJsonObject, payload)
 
 
 class VideoAiLabelNamePayload(BaseModel):
     """Request payload for creating or deleting a label by name."""
 
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     name: str = Field(min_length=1)
 
 
 class VideoAiLabelRenamePayload(BaseModel):
     """Request payload for renaming a label."""
 
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     name_old: str = Field(min_length=1)
     name: str = Field(min_length=1)
 
 
 class VideoAiLabelMutationResponsePayload(BaseModel):
     """Response payload for label create, delete, and rename mutations."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     success: str = Field(min_length=1)
     id: int | None = Field(default=None, ge=1)
     name: str | None = Field(default=None, min_length=1)
 
     def to_response_dict(self) -> VideoAiJsonObject:
         return cast(VideoAiJsonObject, self.model_dump(mode="json", exclude_none=True))
 
 
-def video_ai_json_safe_dict(payload: object) -> VideoAiJsonObject:
+def video_ai_json_safe_dict(payload: Mapping[str, JsonValue]) -> VideoAiJsonObject:
     if not isinstance(payload, Mapping):
         return {}
     mapping = cast(Mapping[object, object], payload)
     return {str(key): cast(JsonValue, value) for key, value in mapping.items()}
 
 
 def validate_video_ai_rerun_prediction_request(
-    payload: object,
+    payload: Mapping[str, JsonValue],
 ) -> VideoAiRerunPredictionRequestPayload:
     return VideoAiRerunPredictionRequestPayload.model_validate(
         video_ai_json_safe_dict(payload)
     )
 
 
-def validate_video_ai_label_name_payload(payload: object) -> VideoAiLabelNamePayload:
+def validate_video_ai_label_name_payload(payload: Mapping[str, JsonValue]) -> VideoAiLabelNamePayload:
     return VideoAiLabelNamePayload.model_validate(video_ai_json_safe_dict(payload))
 
 
 def validate_video_ai_label_rename_payload(
-    payload: object,
+    payload: Mapping[str, JsonValue],
 ) -> VideoAiLabelRenamePayload:
     return VideoAiLabelRenamePayload.model_validate(video_ai_json_safe_dict(payload))
 
 
 __all__ = [
     "VideoAiHuggingFaceModelPayload",
     "VideoAiJsonObject",
     "VideoAiLabelNamePayload",
     "VideoAiLabelMutationResponsePayload",
     "VideoAiLabelPayload",
     "VideoAiLabelRenamePayload",
     "VideoAiLabelSetPayload",
     "VideoAiPredictionJobPayload",
     "VideoAiPredictionModelListPayload",
     "VideoAiPredictionModelMetaPayload",
     "VideoAiRerunPredictionRequestPayload",
     "VideoAiRerunPredictionResponsePayload",
     "validate_video_ai_label_name_payload",
     "validate_video_ai_label_rename_payload",
     "validate_video_ai_rerun_prediction_request",
     "video_ai_json_safe_dict",
 ]
diff --git lx_dtypes/models/contracts/video_correction.py lx_dtypes/models/contracts/video_correction.py
index e2d9ccb..b4db7de 100644
--- lx_dtypes/models/contracts/video_correction.py
+++ lx_dtypes/models/contracts/video_correction.py
@@ -1,302 +1,310 @@
 from __future__ import annotations
 
-from collections.abc import Mapping
+from collections.abc import Mapping, Sequence
 from typing import Literal, TypedDict, TypeAlias, cast
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
 from .json_types import JsonObject, JsonValue
 
 VideoCorrectionMaskType: TypeAlias = Literal["device", "custom"]
 VideoCorrectionProcessingMethod: TypeAlias = Literal[
     "streaming", "direct", "traditional"
 ]
 
 
 class VideoCorrectionRoiData(TypedDict, total=False):
     x: float
     y: float
     width: float
     height: float
     image_width: float | None
     image_height: float | None
 
 
 class VideoCorrectionSegmentUpdateData(TypedDict):
     segments_updated: int
     segments_deleted: int
     segments_unchanged: int
 
 
 class VideoCorrectionRoiPayload(BaseModel):
     model_config = ConfigDict(extra="ignore")
 
     x: float = Field(ge=0)
     y: float = Field(ge=0)
     width: float = Field(ge=0)
     height: float = Field(ge=0)
     image_width: float | None = Field(default=None, ge=0)
     image_height: float | None = Field(default=None, ge=0)
 
     @model_validator(mode="before")
     @classmethod
-    def normalize_endoscope_roi_aliases(cls, value: object) -> object:
+    def normalize_endoscope_roi_aliases(
+        cls, value: Mapping[str, JsonValue] | VideoCorrectionRoiPayload
+    ) -> Mapping[str, JsonValue] | VideoCorrectionRoiPayload:
         if not isinstance(value, Mapping):
             return value
         data = dict(value)
         if {"x", "y", "width", "height"}.issubset(data):
             return data
         if {
             "endoscope_x",
             "endoscope_y",
             "endoscope_width",
             "endoscope_height",
         }.issubset(data):
             return {
                 "x": data.get("endoscope_x"),
                 "y": data.get("endoscope_y"),
                 "width": data.get("endoscope_width"),
                 "height": data.get("endoscope_height"),
                 "image_width": data.get("image_width"),
                 "image_height": data.get("image_height"),
             }
         return data
 
 
-def _blank_to_none(value: object) -> object:
+def _blank_to_none(value: str | int | float | bool | None) -> str | None:
     if isinstance(value, str):
         stripped = value.strip()
         return stripped or None
-    return value
+    if value is None:
+        return None
+    return str(value).strip() or None
 
 
-def _payload_dict(payload: Mapping[str, object]) -> dict[str, object]:
+def _payload_dict(payload: Mapping[str, JsonValue]) -> dict[str, JsonValue]:
     return dict(payload)
 
 
-def _coerce_payload_bool(value: object) -> object:
+def _coerce_payload_bool(value: bool | str | int | None) -> bool | None:
     if value is None or isinstance(value, bool):
         return value
     if isinstance(value, str):
         return value.strip().lower() in {"1", "true", "yes", "on"}
     return bool(value)
 
 
-def _normalize_optional_int_list(value: object) -> object:
+def _normalize_optional_int_list(
+    value: list[int] | tuple[int, ...] | int | str | None
+) -> list[int] | None:
     if value is None:
         return None
     if isinstance(value, list):
-        return [item for item in cast(list[object], value) if item is not None]
-    return [value]
+        return [cast(int, item) for item in cast(list[object], value) if item is not None]
+    return [cast(int, value)]
 
 
 class VideoCorrectionProcessingMethodMixin(BaseModel):
     processing_method: VideoCorrectionProcessingMethod | None = None
     use_streaming: bool | None = None
 
     @field_validator("use_streaming", mode="before")
     @classmethod
-    def normalize_use_streaming(cls, value: object) -> object:
+    def normalize_use_streaming(cls, value: bool | str | int | None) -> bool | None:
         return _coerce_payload_bool(value)
 
     @property
     def resolved_processing_method(self) -> VideoCorrectionProcessingMethod:
         if self.processing_method is not None:
             return self.processing_method
         if self.use_streaming is None:
             return "streaming"
         return "streaming" if self.use_streaming else "direct"
 
 
 class VideoCorrectionApplyMaskPayload(VideoCorrectionProcessingMethodMixin):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     mask_type: VideoCorrectionMaskType = "device"
     device_name: str | None = None
     roi: VideoCorrectionRoiPayload | None = None
     custom_mask: VideoCorrectionRoiPayload | None = None
 
     @field_validator("device_name", mode="before")
     @classmethod
-    def normalize_device_name(cls, value: object) -> object:
+    def normalize_device_name(cls, value: str | int | float | bool | None) -> str | None:
         return _blank_to_none(value)
 
     @model_validator(mode="after")
     def validate_mask_payload(self) -> VideoCorrectionApplyMaskPayload:
         if self.mask_type == "device" and not self.device_name:
             raise ValueError("device_name required for device mask")
         if self.mask_type == "custom" and self.resolved_roi is None:
             raise ValueError("roi required for custom mask")
         return self
 
     @property
     def resolved_roi(self) -> VideoCorrectionRoiPayload | None:
         return self.roi or self.custom_mask
 
     def history_config(self) -> JsonObject:
         roi = self.resolved_roi
         config: JsonObject = {
             "mask_type": self.mask_type,
             "processing_method": self.resolved_processing_method,
         }
         if self.device_name is not None:
             config["device_name"] = self.device_name
         if roi is not None:
             config["roi"] = cast(JsonValue, dump_video_correction_roi_payload(roi))
         return config
 
 
 class VideoCorrectionFrameRemovalPayload(VideoCorrectionProcessingMethodMixin):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     frame_list: list[int] | None = None
     manual_frames: list[int] | None = None
     frame_ranges: str | None = None
     detection_method: str | None = None
     selection_method: str | None = None
 
     @field_validator("frame_list", "manual_frames", mode="before")
     @classmethod
-    def normalize_frame_list(cls, value: object) -> object:
+    def normalize_frame_list(
+        cls, value: list[int] | tuple[int, ...] | int | str | None
+    ) -> list[int] | None:
         return _normalize_optional_int_list(value)
 
     @field_validator(
         "frame_ranges", "detection_method", "selection_method", mode="before"
     )
     @classmethod
-    def normalize_optional_text(cls, value: object) -> object:
+    def normalize_optional_text(cls, value: str | int | float | bool | None) -> str | None:
         return _blank_to_none(value)
 
     @model_validator(mode="after")
     def validate_frame_selection(self) -> VideoCorrectionFrameRemovalPayload:
         if self.frame_list is not None and any(frame < 0 for frame in self.frame_list):
             raise ValueError("frame_list must contain non-negative integers")
         if self.manual_frames is not None and any(
             frame < 0 for frame in self.manual_frames
         ):
             raise ValueError("manual_frames must contain non-negative integers")
         if self.frame_ranges:
             parse_video_correction_frame_ranges(self.frame_ranges)
         return self
 
     @property
     def resolved_detection_method(self) -> str | None:
         if self.detection_method is not None:
             return self.detection_method
         if self.selection_method == "automatic":
             return "automatic"
         return None
 
     def explicit_frames(self) -> list[int] | None:
         if self.frame_list is not None:
             return self.frame_list
         if self.manual_frames is not None:
             return self.manual_frames
         if self.frame_ranges:
             return parse_video_correction_frame_ranges(self.frame_ranges)
         return None
 
 
 class VideoCorrectionSegmentUpdatePayload(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     segments_updated: int = Field(ge=0)
     segments_deleted: int = Field(ge=0)
     segments_unchanged: int = Field(ge=0)
 
 
 class VideoCorrectionErrorPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     error: str = Field(min_length=1)
 
 
 class VideoCorrectionApplyMaskResponsePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     task_id: str | None = None
     output_file: str = Field(min_length=1)
     message: str = Field(min_length=1)
     processing_time: float = Field(ge=0)
 
 
 class VideoCorrectionRemoveFramesResponsePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     task_id: str | None = None
     output_file: str = Field(min_length=1)
     frames_removed: int = Field(ge=0)
     segment_updates: VideoCorrectionSegmentUpdatePayload
     message: str = Field(min_length=1)
     processing_time: float = Field(ge=0)
 
 
 def parse_video_correction_frame_ranges(ranges_str: str) -> list[int]:
     frames: list[int] = []
     for part in ranges_str.split(","):
         normalized_part = part.strip()
         if not normalized_part:
             continue
         if "-" in normalized_part:
             start_text, end_text = normalized_part.split("-", maxsplit=1)
             start = int(start_text)
             end = int(end_text)
             if start < 0 or end < 0:
                 raise ValueError("frame ranges must be non-negative")
             if end < start:
                 raise ValueError(
                     "frame range end must be greater than or equal to start"
                 )
             frames.extend(range(start, end + 1))
         else:
             frame = int(normalized_part)
             if frame < 0:
                 raise ValueError("frame ranges must be non-negative")
             frames.append(frame)
     return sorted(set(frames))
 
 
 def dump_video_correction_roi_payload(
     payload: VideoCorrectionRoiPayload,
 ) -> VideoCorrectionRoiData:
     return cast(
         VideoCorrectionRoiData,
         payload.model_dump(mode="python", exclude_none=True),
     )
 
 
 def dump_video_correction_segment_update_payload(
     payload: VideoCorrectionSegmentUpdatePayload,
 ) -> VideoCorrectionSegmentUpdateData:
     return cast(VideoCorrectionSegmentUpdateData, payload.model_dump(mode="python"))
 
 
 def validate_video_correction_apply_mask_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> VideoCorrectionApplyMaskPayload:
     return VideoCorrectionApplyMaskPayload.model_validate(_payload_dict(payload))
 
 
 def validate_video_correction_frame_removal_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> VideoCorrectionFrameRemovalPayload:
     return VideoCorrectionFrameRemovalPayload.model_validate(_payload_dict(payload))
 
 
 __all__ = [
     "VideoCorrectionApplyMaskPayload",
     "VideoCorrectionApplyMaskResponsePayload",
     "VideoCorrectionErrorPayload",
     "VideoCorrectionFrameRemovalPayload",
     "VideoCorrectionMaskType",
     "VideoCorrectionProcessingMethod",
     "VideoCorrectionRemoveFramesResponsePayload",
     "VideoCorrectionRoiData",
     "VideoCorrectionRoiPayload",
     "VideoCorrectionSegmentUpdateData",
     "VideoCorrectionSegmentUpdatePayload",
     "dump_video_correction_roi_payload",
     "dump_video_correction_segment_update_payload",
     "parse_video_correction_frame_ranges",
     "validate_video_correction_apply_mask_payload",
     "validate_video_correction_frame_removal_payload",
 ]
diff --git lx_dtypes/models/contracts/video_examination.py lx_dtypes/models/contracts/video_examination.py
index 2a980ed..3c618d5 100644
--- lx_dtypes/models/contracts/video_examination.py
+++ lx_dtypes/models/contracts/video_examination.py
@@ -1,144 +1,145 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from datetime import date, datetime
 from typing import NotRequired, TypedDict, cast
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
+from lx_dtypes.models.contracts.json_types import JsonValue
 
 
 class VideoExaminationCreateData(TypedDict):
     video_id: int
     examination_id: int
     date_start: NotRequired[date | None]
     date_end: NotRequired[date | None]
 
 
 class VideoExaminationUpdateData(TypedDict, total=False):
     examination_id: int
     date_start: date | None
     date_end: date | None
 
 
 class VideoExaminationFindingData(TypedDict):
     id: int
     finding_id: int | None
     finding_name: str | None
     created_at: datetime | None
 
 
 class VideoExaminationListQueryData(TypedDict, total=False):
     video_id: int
     patient_id: int
     examination_id: int
 
 
 class VideoExaminationCreatePayload(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     video_id: int = Field(ge=1)
     examination_id: int = Field(ge=1)
     date_start: date | None = None
     date_end: date | None = None
 
 
 class VideoExaminationUpdatePayload(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     examination_id: int | None = Field(default=None, ge=1)
     date_start: date | None = None
     date_end: date | None = None
 
 
 class VideoExaminationFindingPayload(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     id: int = Field(ge=1)
     finding_id: int | None = Field(default=None, ge=1)
     finding_name: str | None = None
     created_at: datetime | None = None
 
 
 class VideoExaminationListQueryPayload(BaseModel):
     model_config = ConfigDict(extra="ignore")
 
     video_id: int | None = Field(default=None, ge=1)
     patient_id: int | None = Field(default=None, ge=1)
     examination_id: int | None = Field(default=None, ge=1)
 
     @field_validator("video_id", "patient_id", "examination_id", mode="before")
     @classmethod
-    def normalize_optional_id(cls, value: object) -> object:
+    def normalize_optional_id(cls, value: int | str | None) -> int | str | None:
         if value in (None, ""):
             return None
         return value
 
 
 class VideoExaminationPathPayload(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     video_id: int = Field(ge=1)
 
 
 def dump_video_examination_create_payload(
     payload: VideoExaminationCreatePayload,
 ) -> VideoExaminationCreateData:
     return cast(
         VideoExaminationCreateData,
         payload.model_dump(exclude_unset=True),
     )
 
 
 def dump_video_examination_update_payload(
     payload: VideoExaminationUpdatePayload,
 ) -> VideoExaminationUpdateData:
     return cast(
         VideoExaminationUpdateData,
         payload.model_dump(exclude_unset=True),
     )
 
 
 def dump_video_examination_finding_payload(
     payload: VideoExaminationFindingPayload,
 ) -> VideoExaminationFindingData:
     return cast(VideoExaminationFindingData, payload.model_dump())
 
 
 def dump_video_examination_list_query_payload(
     payload: VideoExaminationListQueryPayload,
 ) -> VideoExaminationListQueryData:
     return cast(
         VideoExaminationListQueryData,
         payload.model_dump(exclude_none=True),
     )
 
 
 def validate_video_examination_list_query(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> VideoExaminationListQueryPayload:
     return VideoExaminationListQueryPayload.model_validate(dict(payload))
 
 
 def validate_video_examination_path_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> VideoExaminationPathPayload:
     return VideoExaminationPathPayload.model_validate(dict(payload))
 
 
 __all__ = [
     "VideoExaminationCreateData",
     "VideoExaminationCreatePayload",
     "VideoExaminationFindingData",
     "VideoExaminationFindingPayload",
     "VideoExaminationListQueryData",
     "VideoExaminationListQueryPayload",
     "VideoExaminationPathPayload",
     "VideoExaminationUpdateData",
     "VideoExaminationUpdatePayload",
     "dump_video_examination_create_payload",
     "dump_video_examination_finding_payload",
     "dump_video_examination_list_query_payload",
     "dump_video_examination_update_payload",
     "validate_video_examination_list_query",
     "validate_video_examination_path_payload",
 ]
diff --git lx_dtypes/models/contracts/video_export.py lx_dtypes/models/contracts/video_export.py
index f441af9..92a4601 100644
--- lx_dtypes/models/contracts/video_export.py
+++ lx_dtypes/models/contracts/video_export.py
@@ -1,177 +1,177 @@
 from __future__ import annotations
 
 from typing import Annotated, Literal, TypeAlias, TypedDict
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 PositiveInt = Annotated[int, Field(ge=1)]
 VideoAnnotationExportFormat: TypeAlias = Literal["csv", "json"]
 
 
 class VideoAnnotationExportConfigUpdateData(TypedDict, total=False):
     output_path: str
     output_dir: str
     output_format: VideoAnnotationExportFormat
     video_id: int
     label_id: int
     information_source_name: str
     only_true: bool
     limit: int
     load_base_data: bool
     export_videos: bool
     export_frames: bool
     transcode_frames: bool
     transcode_fps: float
     transcode_quality: int
     transcode_ext: str
     transcode_overwrite: bool
     use_frame_pk_paths: bool
     use_export_flags: bool
     segment_ids: list[int]
     center_key: str | None
     all_centers: bool
     only_validated: bool
 
 
 class VideoAnnotationExportRequestPayload(BaseModel):
     """Validated request payload for the annotated video export endpoint."""
 
     model_config = ConfigDict(extra="ignore")
 
     config_path: str | None = None
     output_path: str | None = None
     output_dir: str | None = None
     output_format: VideoAnnotationExportFormat | None = None
     format: VideoAnnotationExportFormat | None = None
     video_id: PositiveInt | None = None
     label_id: PositiveInt | None = None
     information_source_name: str | None = None
     only_true: bool | None = None
     limit: int | None = Field(default=None, ge=0)
     load_base_data: bool | None = None
     export_videos: bool | None = None
     export_frames: bool | None = None
     transcode_frames: bool | None = None
     transcode_fps: float | None = Field(default=None, gt=0.0)
     transcode_quality: int | None = Field(default=None, ge=1)
     transcode_ext: str | None = Field(default=None, min_length=1)
     transcode_overwrite: bool | None = None
     use_frame_pk_paths: bool | None = None
     use_export_flags: bool | None = None
     segment_ids: list[PositiveInt] | None = None
     center_key: str | None = None
     all_centers: bool | None = None
     only_validated: bool | None = None
 
     @field_validator(
         "only_true",
         "load_base_data",
         "export_videos",
         "export_frames",
         "transcode_frames",
         "transcode_overwrite",
         "use_frame_pk_paths",
         "use_export_flags",
         "all_centers",
         "only_validated",
         mode="before",
     )
     @classmethod
-    def _coerce_payload_bool(cls, value: object) -> object:
+    def _coerce_payload_bool(cls, value: bool | str | int | None) -> bool | str | int | None:
         if value is None or isinstance(value, bool):
             return value
         if isinstance(value, str):
             return value.strip().lower() in {"1", "true", "yes", "on"}
         return bool(value)
 
 
 class VideoAnnotationExportResultPayload(BaseModel):
     """Validated success response payload for annotated video exports."""
 
     model_config = ConfigDict(extra="forbid")
 
     success: bool
     output_path: str = Field(min_length=1)
     row_count: int = Field(ge=0)
     exported_video_count: int = Field(ge=0)
     exported_frame_count: int = Field(ge=0)
     video_output_dir: str | None = None
     frame_output_dir: str | None = None
 
 
 class VideoAnnotationExportErrorPayload(BaseModel):
     """Validated error response payload for annotated video exports."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     success: Literal[False] = False
     error: str = Field(min_length=1)
 
 
 def dump_video_annotation_export_update_payload(
     payload: VideoAnnotationExportRequestPayload,
 ) -> VideoAnnotationExportConfigUpdateData:
     fields_set = payload.model_fields_set
     data: VideoAnnotationExportConfigUpdateData = {}
 
     if "output_path" in fields_set and payload.output_path is not None:
         data["output_path"] = payload.output_path
     if "output_dir" in fields_set and payload.output_dir is not None:
         data["output_dir"] = payload.output_dir
     if "output_format" in fields_set and payload.output_format is not None:
         data["output_format"] = payload.output_format
     elif "format" in fields_set and payload.format is not None:
         data["output_format"] = payload.format
     if "video_id" in fields_set and payload.video_id is not None:
         data["video_id"] = payload.video_id
     if "label_id" in fields_set and payload.label_id is not None:
         data["label_id"] = payload.label_id
     if (
         "information_source_name" in fields_set
         and payload.information_source_name is not None
     ):
         data["information_source_name"] = payload.information_source_name
     if "only_true" in fields_set and payload.only_true is not None:
         data["only_true"] = payload.only_true
     if "limit" in fields_set and payload.limit is not None:
         data["limit"] = payload.limit
     if "load_base_data" in fields_set and payload.load_base_data is not None:
         data["load_base_data"] = payload.load_base_data
     if "export_videos" in fields_set and payload.export_videos is not None:
         data["export_videos"] = payload.export_videos
     if "export_frames" in fields_set and payload.export_frames is not None:
         data["export_frames"] = payload.export_frames
     if "transcode_frames" in fields_set and payload.transcode_frames is not None:
         data["transcode_frames"] = payload.transcode_frames
     if "transcode_fps" in fields_set and payload.transcode_fps is not None:
         data["transcode_fps"] = payload.transcode_fps
     if "transcode_quality" in fields_set and payload.transcode_quality is not None:
         data["transcode_quality"] = payload.transcode_quality
     if "transcode_ext" in fields_set and payload.transcode_ext is not None:
         data["transcode_ext"] = payload.transcode_ext
     if "transcode_overwrite" in fields_set and payload.transcode_overwrite is not None:
         data["transcode_overwrite"] = payload.transcode_overwrite
     if "use_frame_pk_paths" in fields_set and payload.use_frame_pk_paths is not None:
         data["use_frame_pk_paths"] = payload.use_frame_pk_paths
     if "use_export_flags" in fields_set and payload.use_export_flags is not None:
         data["use_export_flags"] = payload.use_export_flags
     if "segment_ids" in fields_set and payload.segment_ids is not None:
         data["segment_ids"] = payload.segment_ids
     if "center_key" in fields_set and payload.center_key is not None:
         data["center_key"] = payload.center_key.strip() or None
     if "all_centers" in fields_set and payload.all_centers is not None:
         data["all_centers"] = payload.all_centers
     if "only_validated" in fields_set and payload.only_validated is not None:
         data["only_validated"] = payload.only_validated
 
     return data
 
 
 __all__ = [
     "VideoAnnotationExportConfigUpdateData",
     "VideoAnnotationExportErrorPayload",
     "VideoAnnotationExportFormat",
     "VideoAnnotationExportRequestPayload",
     "VideoAnnotationExportResultPayload",
     "dump_video_annotation_export_update_payload",
 ]
diff --git lx_dtypes/models/contracts/video_file.py lx_dtypes/models/contracts/video_file.py
index 4782cff..e53024c 100644
--- lx_dtypes/models/contracts/video_file.py
+++ lx_dtypes/models/contracts/video_file.py
@@ -1,89 +1,91 @@
 # lx_dtypes/models/contracts/video_file.py
 from __future__ import annotations
 
 from datetime import datetime
 from typing import Literal, TypeAlias
 
-from pydantic import BaseModel, ConfigDict, Field, field_validator
+from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
 
 from lx_dtypes.models.contracts.json_types import JsonNull, JsonValue
 
 
 FrameSourceMode: TypeAlias = Literal["cache", "stream", "auto"]
 
 type VideoFileMetaJsonValue = (
     JsonValue
     | JsonNull
-    | list[VideoFileMetaJsonValue]  # Look, no quotes needed!
+    | list[VideoFileMetaJsonValue]
     | dict[str, VideoFileMetaJsonValue]
 )
 
 type VideoFileMetaJsonObject = dict[str, VideoFileMetaJsonValue]
 
 
 class VideoFileIdentityPayload(BaseModel):
     """Stable identity/reference fields for a VideoFile-like object."""
 
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     pk: int | None = Field(default=None, ge=1)
     id: int | None = Field(default=None, ge=1)
     video_hash: str = Field(min_length=1)
     original_file_name: str | None = None
 
     @field_validator("id", mode="after")
     @classmethod
-    def _id_or_pk(cls, value: int | None, info: object) -> int | None:
+    def _id_or_pk(
+        cls, value: int | None, info: ValidationInfo
+    ) -> int | None:
         return value
 
 
 class VideoFileTechnicalMetadataPayload(BaseModel):
     """Technical scalar metadata from VideoFile / ffmpeg / import state."""
 
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     fps: float | None = Field(default=None, gt=0)
     duration: float | None = Field(default=None, ge=0)
     frame_count: int | None = Field(default=None, ge=0)
     width: int | None = Field(default=None, ge=1)
     height: int | None = Field(default=None, ge=1)
     suffix: str | None = None
     frame_dir: str = ""
 
 
 class VideoFileStoragePayload(BaseModel):
     """Storage and stream path metadata that is safe to pass across boundaries."""
 
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     storage_mode: str = ""
     raw_streamable_relative_path: str = ""
     processed_streamable_relative_path: str = ""
     has_raw: bool = False
     is_processed: bool = False
 
 
 class VideoFilePayload(
     VideoFileIdentityPayload,
     VideoFileTechnicalMetadataPayload,
     VideoFileStoragePayload,
 ):
     """Serializable VideoFile contract without Django ORM relations or methods."""
 
     model_config = ConfigDict(extra="ignore", frozen=True, strict=True)
 
     uploaded_at: datetime | None = None
     date_created: datetime | None = None
     date_modified: datetime | None = None
     meta: VideoFileMetaJsonObject | None = None
 
 
 __all__ = [
     "FrameSourceMode",
     "VideoFileIdentityPayload",
     "VideoFileMetaJsonObject",
     "VideoFileMetaJsonValue",
     "VideoFilePayload",
     "VideoFileStoragePayload",
     "VideoFileTechnicalMetadataPayload",
 ]
diff --git lx_dtypes/models/contracts/video_format.py lx_dtypes/models/contracts/video_format.py
index f0b4901..9114ae6 100644
--- lx_dtypes/models/contracts/video_format.py
+++ lx_dtypes/models/contracts/video_format.py
@@ -1,32 +1,32 @@
 from __future__ import annotations
 
 from pydantic import BaseModel, ConfigDict, field_validator
 
 
 class VideoFormatInfo(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     video_codec: str = "unknown"
     pixel_format: str = "unknown"
     width: int = 0
     height: int = 0
     has_audio: bool = True
     container: str = "unknown"
     can_stream_copy: bool = False
 
     @field_validator("video_codec", "pixel_format", "container", mode="before")
     @classmethod
-    def normalize_text(cls, value: object) -> str:
+    def normalize_text(cls, value: str | int | float | bool | None) -> str:
         normalized = str(value).strip()
         return normalized or "unknown"
 
     @field_validator("width", "height", mode="before")
     @classmethod
-    def normalize_size(cls, value: object) -> int:
+    def normalize_size(cls, value: int | str | float | bool | None) -> int:
         normalized = int(str(value))
         if normalized < 0:
             raise ValueError("size values must be >= 0")
         return normalized
 
 
 __all__ = ["VideoFormatInfo"]
diff --git lx_dtypes/models/contracts/video_frame_annotations.py lx_dtypes/models/contracts/video_frame_annotations.py
index 59a7ee7..029e16e 100644
--- lx_dtypes/models/contracts/video_frame_annotations.py
+++ lx_dtypes/models/contracts/video_frame_annotations.py
@@ -1,252 +1,267 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from typing import NotRequired, TypedDict, cast
 
 from pydantic import (
     BaseModel,
     ConfigDict,
     Field,
     RootModel,
     field_validator,
     model_validator,
 )
 
-from .json_types import JsonObject
+from .json_types import JsonObject, JsonValue
 
 
 class FrameAnnotationBulkItemData(TypedDict):
     frame_id: int
     label_id: int
     value: bool
     float_value: NotRequired[float | None]
     information_source_name: str
     annotator: NotRequired[str | None]
     external_annotation_id: NotRequired[str | None]
     model_meta_id: NotRequired[int | None]
 
 
 class FrameAnnotationBulkEnvelopeData(TypedDict, total=False):
     video_id: int | None
     ai_dataset_id: int | None
     annotations: list[JsonObject]
 
 
 class FrameBoxAnnotationBulkItemData(FrameAnnotationBulkItemData, total=False):
     id: int | None
     x: float
     y: float
     width: float
     height: float
     image_width: int
     image_height: int
 
 
 class FrameBoxAnnotationBulkEnvelopeData(TypedDict, total=False):
     frame_id: int | None
     video_id: int | None
     replace: bool
     annotator: str | None
     information_source_name: str | None
     information_source: str | None
     annotations: list[JsonObject]
 
 
 class FrameAnnotationBulkItemPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     frame_id: int = Field(ge=1)
     label_id: int | None = Field(default=None, ge=1)
     choice_name: str | None = None
     value: bool = True
     float_value: float | None = None
     information_source_name: str = Field(min_length=1)
     annotator: str | None = None
     external_annotation_id: str | None = None
     model_meta_id: int | None = Field(default=None, ge=1)
 
     @field_validator(
         "choice_name", "annotator", "external_annotation_id", mode="before"
     )
     @classmethod
-    def _blank_to_none(cls, value: object) -> object:
+    def _blank_to_none(cls, value: str | int | float | bool | None) -> str | None:
         if isinstance(value, str):
             return value.strip() or None
-        return value
+        if value is None:
+            return None
+        return str(value).strip() or None
 
     @model_validator(mode="after")
     def validate_label_reference(self) -> FrameAnnotationBulkItemPayload:
         if self.label_id is None and not self.choice_name:
             raise ValueError("Either label_id or choice_name is required")
         return self
 
 
 class FrameAnnotationBulkEnvelopePayload(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     video_id: int | None = Field(default=None, ge=1)
     ai_dataset_id: int | None = Field(default=None, ge=1)
     annotations: list[FrameAnnotationBulkItemPayload]
 
 
 class FrameAnnotationSkipPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     frame_id: int = Field(ge=1)
     video_id: int | None = Field(default=None, ge=1)
     annotator: str | None = None
     reason: str = ""
     information_source_name: str | None = None
     information_source: str | None = None
     exclude_annotated: bool = True
 
     @field_validator(
         "annotator", "information_source_name", "information_source", mode="before"
     )
     @classmethod
-    def _blank_to_none(cls, value: object) -> object:
+    def _blank_to_none(cls, value: str | int | float | bool | None) -> str | None:
         if isinstance(value, str):
             return value.strip() or None
-        return value
+        if value is None:
+            return None
+        return str(value).strip() or None
 
 
 class FrameBoxAnnotationBulkItemPayload(FrameAnnotationBulkItemPayload):
     id: int | None = Field(default=None, ge=1)
     x: float = Field(ge=0)
     y: float = Field(ge=0)
     width: float = Field(gt=0)
     height: float = Field(gt=0)
     image_width: int = Field(gt=0)
     image_height: int = Field(gt=0)
 
     @model_validator(mode="after")
     def validate_box_bounds(self) -> FrameBoxAnnotationBulkItemPayload:
         if self.x + self.width > self.image_width:
             raise ValueError("x + width must not exceed image_width")
         if self.y + self.height > self.image_height:
             raise ValueError("y + height must not exceed image_height")
         return self
 
 
 class FrameBoxAnnotationBulkEnvelopePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     frame_id: int | None = Field(default=None, ge=1)
     video_id: int | None = Field(default=None, ge=1)
     replace: bool = False
     annotator: str | None = None
     information_source_name: str | None = None
     information_source: str | None = None
     annotations: list[FrameBoxAnnotationBulkItemPayload]
 
     @model_validator(mode="before")
     @classmethod
-    def normalize_item_frame_ids(cls, value: object) -> object:
+    def normalize_item_frame_ids(
+        cls,
+        value: Mapping[str, JsonValue] | None,
+    ) -> Mapping[str, JsonValue] | None:
         if not isinstance(value, Mapping):
             return value
 
-        mapping = cast(Mapping[object, object], value)
+        mapping = cast(Mapping[object, JsonValue], value)
         outer_frame_id = mapping.get("frame_id")
         wrapper_source = mapping.get("information_source_name")
         if isinstance(wrapper_source, str):
             wrapper_source = wrapper_source.strip() or None
         if wrapper_source is None:
             wrapper_source = mapping.get("information_source")
             if isinstance(wrapper_source, str):
                 wrapper_source = wrapper_source.strip() or None
         if wrapper_source is None:
             wrapper_source = "manual_annotation"
         wrapper_annotator = mapping.get("annotator")
         if isinstance(wrapper_annotator, str):
             wrapper_annotator = wrapper_annotator.strip() or None
         annotations = mapping.get("annotations")
         if not isinstance(annotations, list):
             return value
 
-        normalized_annotations: list[object] = []
+        normalized_envelope = {
+            str(key): cast(JsonValue, val) for key, val in mapping.items()
+        }
+
+        normalized_annotations: list[JsonValue] = []
         for item in annotations:
             if not isinstance(item, Mapping):
-                normalized_annotations.append(item)
+                normalized_annotations.append(cast(JsonValue, item))
                 continue
-            item_mapping = dict(item)
+            item_mapping = {
+                str(key): cast(JsonValue, value)
+                for key, value in item.items()
+            }
             if outer_frame_id is not None and "frame_id" not in item_mapping:
-                item_mapping["frame_id"] = outer_frame_id
+                item_mapping["frame_id"] = cast(JsonValue, outer_frame_id)
             if "information_source_name" not in item_mapping:
-                item_mapping["information_source_name"] = wrapper_source
+                item_mapping["information_source_name"] = cast(JsonValue, wrapper_source)
             if wrapper_annotator is not None and "annotator" not in item_mapping:
-                item_mapping["annotator"] = wrapper_annotator
-            normalized_annotations.append(item_mapping)
+                item_mapping["annotator"] = cast(JsonValue, wrapper_annotator)
+            normalized_annotations.append(cast(JsonValue, item_mapping))
 
-        normalized_envelope = dict(mapping)
-        normalized_envelope["annotations"] = normalized_annotations
+        normalized_envelope["annotations"] = cast(JsonValue, normalized_annotations)
         return normalized_envelope
 
     @model_validator(mode="after")
     def validate_item_frame_ids(self) -> FrameBoxAnnotationBulkEnvelopePayload:
         if self.frame_id is None:
             return self
         if any(item.frame_id != self.frame_id for item in self.annotations):
             raise ValueError("annotation frame_id must match the envelope frame_id")
         return self
 
     @field_validator(
         "annotator", "information_source_name", "information_source", mode="before"
     )
     @classmethod
-    def _blank_to_none(cls, value: object) -> object:
+    def _blank_to_none(
+        cls, value: str | int | float | bool | None
+    ) -> str | None:
         if isinstance(value, str):
             return value.strip() or None
-        return value
+        return str(value)
 
     @property
     def resolved_information_source_name(self) -> str | None:
         return self.information_source_name or self.information_source
 
 
 class FrameAnnotationPayloadMapping(RootModel[JsonObject]):
     model_config = ConfigDict(frozen=True)
 
     @field_validator("root", mode="before")
     @classmethod
-    def normalize_mapping(cls, value: object) -> object:
+    def normalize_mapping(cls, value: Mapping[str, JsonValue]) -> JsonObject:
         if not isinstance(value, Mapping):
             raise ValueError("payload must be a JSON object")
-        mapping = cast(Mapping[object, object], value)
+        mapping = cast(Mapping[object, JsonValue], value)
         return {str(key): item for key, item in mapping.items()}
 
     def to_json_object(self) -> JsonObject:
         return cast(JsonObject, self.model_dump(mode="json"))
 
 
 def dump_frame_annotation_bulk_item(
     payload: FrameAnnotationBulkItemPayload,
 ) -> FrameAnnotationBulkItemData:
     return cast(
         FrameAnnotationBulkItemData,
         payload.model_dump(mode="python", exclude_none=True),
     )
 
 
 def dump_frame_box_annotation_bulk_item(
     payload: FrameBoxAnnotationBulkItemPayload,
 ) -> FrameBoxAnnotationBulkItemData:
     return cast(
         FrameBoxAnnotationBulkItemData,
         payload.model_dump(mode="python", exclude_none=True),
     )
 
 
 __all__ = [
     "FrameAnnotationBulkEnvelopeData",
     "FrameAnnotationBulkEnvelopePayload",
     "FrameAnnotationBulkItemData",
     "FrameAnnotationBulkItemPayload",
     "FrameAnnotationPayloadMapping",
     "FrameAnnotationSkipPayload",
     "FrameBoxAnnotationBulkEnvelopeData",
     "FrameBoxAnnotationBulkEnvelopePayload",
     "FrameBoxAnnotationBulkItemData",
     "FrameBoxAnnotationBulkItemPayload",
     "dump_frame_annotation_bulk_item",
     "dump_frame_box_annotation_bulk_item",
 ]
diff --git lx_dtypes/models/contracts/video_frame_box_annotations.py lx_dtypes/models/contracts/video_frame_box_annotations.py
index e481a2c..8bff361 100644
--- lx_dtypes/models/contracts/video_frame_box_annotations.py
+++ lx_dtypes/models/contracts/video_frame_box_annotations.py
@@ -1,126 +1,139 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from typing import TypeAlias, cast
 
 from pydantic import BaseModel, ConfigDict, Field
 
-from .json_types import JsonNumericObject, JsonScalar
+from .json_types import JsonNumericObject, JsonObject, JsonScalar, JsonValue
 from .video_frame_annotations import FrameBoxAnnotationBulkEnvelopePayload
 
 VideoFrameBoxJsonObject: TypeAlias = JsonNumericObject
 
 
 def _empty_annotation_list() -> list[VideoFrameBoxJsonObject]:
     return []
 
 
-def _normalize_mapping(value: Mapping[object, object]) -> VideoFrameBoxJsonObject:
-    return {str(key): cast(JsonScalar, item) for key, item in value.items()}
+def _normalize_mapping(
+    value: Mapping[object, JsonValue],
+) -> VideoFrameBoxJsonObject:
+    return {
+        str(key): _normalize_scalar(item)
+        for key, item in value.items()
+    }
+
+
+def _normalize_scalar(value: JsonValue) -> JsonScalar:
+    if isinstance(value, (str, int, float, bool)):
+        return value
+    return ""
 
 
 VideoFrameBoxAnnotationRequestPayload = FrameBoxAnnotationBulkEnvelopePayload
 
 
 class VideoFrameBoxAnnotationListResponsePayload(BaseModel):
     """Response payload for listing frame box annotations."""
 
     model_config = ConfigDict(extra="forbid")
 
     status: str = "success"
     frame_id: int = Field(ge=1)
     video_id: int = Field(ge=1)
     annotations: list[VideoFrameBoxJsonObject] = Field(
         default_factory=_empty_annotation_list
     )
     count: int = Field(ge=0)
 
     def to_response_dict(self) -> VideoFrameBoxJsonObject:
         return cast(VideoFrameBoxJsonObject, self.model_dump(mode="json"))
 
 
 class VideoFrameBoxAnnotationMutationResponsePayload(BaseModel):
     """Response payload for frame box annotation upsert or delete operations."""
 
     model_config = ConfigDict(extra="forbid")
 
     status: str = "success"
     video_id: int | None = Field(default=None, ge=1)
     upserted_count: int = Field(ge=0)
     deleted_count: int | None = Field(default=None, ge=0)
     annotations: list[VideoFrameBoxJsonObject] = Field(
         default_factory=_empty_annotation_list
     )
 
     def to_response_dict(self) -> VideoFrameBoxJsonObject:
         return cast(
             VideoFrameBoxJsonObject,
             self.model_dump(mode="json", exclude_none=True),
         )
 
 
 class VideoPhiRegionPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", strict=True, str_strip_whitespace=True)
 
     x: float = Field(ge=0)
     y: float = Field(ge=0)
     width: float = Field(gt=0)
     height: float = Field(gt=0)
     source: str = "phi_detector"
     confidence: float | None = None
 
 
 class VideoPhiFrameObservationPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", strict=True)
 
     frame_number: int | None = Field(default=None, ge=0)
     frame_id: int | None = Field(default=None, ge=0)
     image_width: int = Field(ge=1)
     image_height: int = Field(ge=1)
     phi_regions: list[VideoPhiRegionPayload] = Field(default_factory=list)
 
     @property
     def resolved_frame_number(self) -> int | None:
         return self.frame_number if self.frame_number is not None else self.frame_id
 
 
 def validate_video_phi_frame_observations(
     payload: object,
 ) -> list[VideoPhiFrameObservationPayload]:
     if payload is None:
         return []
     if not isinstance(payload, list):
         raise ValueError("frame_observations must be a list")
     observations: list[VideoPhiFrameObservationPayload] = []
     for item in payload:
         observations.append(VideoPhiFrameObservationPayload.model_validate(item))
     return observations
 
 
-def video_frame_box_json_safe_dict(payload: object) -> VideoFrameBoxJsonObject:
+def video_frame_box_json_safe_dict(
+    payload: JsonObject,
+) -> VideoFrameBoxJsonObject:
     if not isinstance(payload, Mapping):
         return {}
-    return _normalize_mapping(payload)
+    return _normalize_mapping(cast(Mapping[object, JsonValue], payload))
 
 
 def validate_video_frame_box_annotation_request(
-    payload: object,
+    payload: JsonObject | list[JsonObject],
 ) -> VideoFrameBoxAnnotationRequestPayload:
     if isinstance(payload, list):
         return VideoFrameBoxAnnotationRequestPayload.model_validate(
             {"annotations": payload}
         )
     return VideoFrameBoxAnnotationRequestPayload.model_validate(payload)
 
 
 __all__ = [
     "VideoFrameBoxAnnotationListResponsePayload",
     "VideoFrameBoxAnnotationMutationResponsePayload",
     "VideoFrameBoxAnnotationRequestPayload",
     "VideoFrameBoxJsonObject",
     "VideoPhiFrameObservationPayload",
     "VideoPhiRegionPayload",
     "validate_video_phi_frame_observations",
     "validate_video_frame_box_annotation_request",
     "video_frame_box_json_safe_dict",
 ]
diff --git lx_dtypes/models/contracts/video_frame_cache.py lx_dtypes/models/contracts/video_frame_cache.py
index 06b128d..aadb1a2 100644
--- lx_dtypes/models/contracts/video_frame_cache.py
+++ lx_dtypes/models/contracts/video_frame_cache.py
@@ -1,59 +1,60 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from typing import cast
+from lx_dtypes.models.contracts.json_types import JsonValue
 
 from pydantic import BaseModel, ConfigDict, Field
 
 FrameCacheLogValue = str | int | float | bool | list[int] | list[str]
 FrameCacheLogPayload = dict[str, FrameCacheLogValue]
 
 
 class FrameCacheManifestLogPayload(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True)
 
     frame_dir: str
     file_count: int
     missing_frame_numbers: list[int] = Field(default_factory=list)
     extra_frame_numbers: list[int] = Field(default_factory=list)
     invalid_file_names: list[str] = Field(default_factory=list)
     duplicate_frame_numbers: list[int] = Field(default_factory=list)
     unexpected_file_names: list[str] = Field(default_factory=list)
     expected_count: int | None = None
 
     def as_log_payload(self) -> FrameCacheLogPayload:
         return cast(
             FrameCacheLogPayload,
             self.model_dump(mode="python", exclude_none=True),
         )
 
 
 class FrameCacheValidationLogPayload(FrameCacheManifestLogPayload):
     db_extracted_frame_count: int = 0
     db_missing_frame_numbers: list[int] = Field(default_factory=list)
     db_extra_frame_numbers: list[int] = Field(default_factory=list)
     db_path_mismatch_frame_numbers: list[int] = Field(default_factory=list)
     db_missing_file_frame_numbers: list[int] = Field(default_factory=list)
     valid: bool = False
 
 
 def parse_frame_cache_manifest_payload(
-    payload: Mapping[str, object] | None,
+    payload: Mapping[str, JsonValue] | None,
 ) -> FrameCacheManifestLogPayload:
     return FrameCacheManifestLogPayload.model_validate(payload or {})
 
 
 def parse_frame_cache_validation_payload(
-    payload: Mapping[str, object] | None,
+    payload: Mapping[str, JsonValue] | None,
 ) -> FrameCacheValidationLogPayload:
     return FrameCacheValidationLogPayload.model_validate(payload or {})
 
 
 __all__ = [
     "FrameCacheLogPayload",
     "FrameCacheLogValue",
     "FrameCacheManifestLogPayload",
     "FrameCacheValidationLogPayload",
     "parse_frame_cache_manifest_payload",
     "parse_frame_cache_validation_payload",
 ]
diff --git lx_dtypes/models/contracts/video_prediction_logic.py lx_dtypes/models/contracts/video_prediction_logic.py
index 380b93a..81599ef 100644
--- lx_dtypes/models/contracts/video_prediction_logic.py
+++ lx_dtypes/models/contracts/video_prediction_logic.py
@@ -1,17 +1,18 @@
 from __future__ import annotations
 
 from pydantic import BaseModel, ConfigDict, Field
+from .json_types import JsonValue
 
 
 class PredictionSegmentCreatePayload(BaseModel):
     model_config = ConfigDict(extra="forbid", frozen=True, arbitrary_types_allowed=True)
 
     start_frame_number: int = Field(ge=0)
     end_frame_number: int = Field(ge=0)
-    source: object
-    label: object
-    prediction_meta: object
-    video_file: object
+    source: JsonValue
+    label: JsonValue
+    prediction_meta: JsonValue
+    video_file: JsonValue
 
 
 __all__ = ["PredictionSegmentCreatePayload"]
diff --git lx_dtypes/models/contracts/video_processing.py lx_dtypes/models/contracts/video_processing.py
index a79d6b2..bc1c204 100644
--- lx_dtypes/models/contracts/video_processing.py
+++ lx_dtypes/models/contracts/video_processing.py
@@ -1,104 +1,106 @@
 from __future__ import annotations
 
 from pydantic import BaseModel, ConfigDict, field_validator, model_validator
 
 from .endoscopy_processor import RoiBoxCore
 
 
 class VideoEncoderConfig(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     name: str
     preset_param: str
     preset_value: str
     quality_param: str
     quality_value: str
     type: str
     fallback_preset: str
 
     @field_validator(
         "name",
         "preset_param",
         "preset_value",
         "quality_param",
         "quality_value",
         "type",
         "fallback_preset",
         mode="before",
     )
     @classmethod
-    def normalize_non_empty_text(cls, value: object) -> str:
+    def normalize_non_empty_text(
+        cls, value: str | int | float | bool | None
+    ) -> str:
         normalized = str(value).strip()
         if not normalized:
             raise ValueError("value must not be empty")
         return normalized
 
 
 class VideoMaskConfig(BaseModel):
     model_config = ConfigDict(extra="forbid")
 
     x: int
     y: int
     width: int
     height: int
     image_width: int = 1920
     image_height: int = 1080
 
     @field_validator("x", "y", "width", "height", "image_width", "image_height")
     @classmethod
-    def validate_int(cls, value: object) -> int:
+    def validate_int(cls, value: str | int | float | bool | None) -> int:
         normalized = int(str(value))
         if normalized < 0:
             raise ValueError("value must be >= 0")
         return normalized
 
     @model_validator(mode="after")
     def validate_dimensions(self) -> "VideoMaskConfig":
         if self.width <= 0:
             raise ValueError("width must be > 0")
         if self.height <= 0:
             raise ValueError("height must be > 0")
         if self.image_width <= 0:
             raise ValueError("image_width must be > 0")
         if self.image_height <= 0:
             raise ValueError("image_height must be > 0")
         if self.x + self.width > self.image_width:
             raise ValueError("mask width exceeds image width")
         if self.y + self.height > self.image_height:
             raise ValueError("mask height exceeds image height")
         return self
 
 
 class VideoMaskRegionCore(RoiBoxCore):
     model_config = ConfigDict(extra="forbid", strict=True)
 
     image_width: int
     image_height: int
     configured_x: int
     configured_y: int
     configured_width: int
     configured_height: int
 
     @field_validator("image_width", "image_height")
     @classmethod
     def validate_image_dimension(cls, value: int) -> int:
         if value <= 0:
             raise ValueError("image dimensions must be > 0")
         return value
 
     @field_validator("configured_x", "configured_y")
     @classmethod
     def validate_config_origin(cls, value: int) -> int:
         if value < 0:
             raise ValueError("configured x and y must be >= 0")
         return value
 
     @field_validator("configured_width", "configured_height")
     @classmethod
     def validate_config_size(cls, value: int) -> int:
         if value <= 0:
             raise ValueError("configured width and height must be > 0")
         return value
 
 
 __all__ = ["VideoEncoderConfig", "VideoMaskConfig", "VideoMaskRegionCore"]
diff --git lx_dtypes/models/contracts/video_reimport.py lx_dtypes/models/contracts/video_reimport.py
index d3bdf37..8e0e3f1 100644
--- lx_dtypes/models/contracts/video_reimport.py
+++ lx_dtypes/models/contracts/video_reimport.py
@@ -1,271 +1,276 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from typing import Literal, TypeAlias, TypedDict, cast
 
 from pydantic import BaseModel, ConfigDict, Field, field_validator
 
 from .json_types import JsonObject as BaseJsonObject
 from .json_types import JsonValue as BaseJsonValue
 
 
 VideoReimportOperation: TypeAlias = Literal["video_reimport"]
 VideoReimportDispatchStatus: TypeAlias = Literal[
     "queued",
     "already_queued",
     "busy",
     "completed",
     "failed",
     "lost",
 ]
 VideoReimportStatus: TypeAlias = VideoReimportDispatchStatus
 VideoReimportApiStatus: TypeAlias = Literal[
     "queued",
     "already_queued",
     "busy",
     "completed",
     "failed",
     "lost",
     "done",
 ]
 VideoReimportErrorType: TypeAlias = Literal[
     "integrity_lost",
     "missing_source",
     "dispatch_error",
     "media_busy",
     "processing_error",
     "storage_error",
     "validation_error",
 ]
 VideoReimportJobMode: TypeAlias = Literal["celery", "inline"]
 VideoReimportPredictionRefreshStatus: TypeAlias = Literal[
     "skipped",
     "not_queued",
     "failed",
 ]
 VideoReimportJsonValue: TypeAlias = BaseJsonValue
 JsonValue: TypeAlias = VideoReimportJsonValue
 JsonObject: TypeAlias = BaseJsonObject
 VideoReimportRequestData: TypeAlias = JsonObject
 
 VIDEO_REIMPORT_OPERATION: VideoReimportOperation = "video_reimport"
 VIDEO_REIMPORT_HISTORY_KIND: VideoReimportOperation = VIDEO_REIMPORT_OPERATION
 
 
 def _empty_json_object() -> JsonObject:
     return {}
 
 
 class VideoReimportRequestPayload(BaseModel):
     """Validated request payload for video re-import endpoints."""
 
     model_config = ConfigDict(extra="allow", str_strip_whitespace=True)
 
     refresh_predictions: bool | None = None
     model_meta_id: int | None = Field(default=None, ge=1)
     model_name: str | None = None
     model_meta_version: str | None = None
     test_run: bool | None = None
     n_test_frames: int | None = Field(default=None, ge=1)
     delete_frames_after: bool | None = None
 
     @field_validator(
         "refresh_predictions",
         "test_run",
         "delete_frames_after",
         mode="before",
     )
     @classmethod
-    def _normalize_optional_bool(cls, value: object) -> object:
+    def _normalize_optional_bool(cls, value: bool | str | int | None) -> bool | str | int | None:
         if value is None or isinstance(value, bool):
             return value
         if isinstance(value, str):
             normalized = value.strip().lower()
             if normalized in {"1", "true", "yes", "y", "on"}:
                 return True
             if normalized in {"0", "false", "no", "n", "off"}:
                 return False
         return value
 
     @field_validator("model_name", "model_meta_version", mode="before")
     @classmethod
-    def _blank_to_none(cls, value: object) -> object:
+    def _blank_to_none(cls, value: str | int | float | bool | None) -> str | None:
         if isinstance(value, str):
             return value.strip() or None
         if value is None:
             return None
         return str(value)
 
     def to_payload_dict(self) -> JsonObject:
         return cast(
             JsonObject,
             self.model_dump(mode="json", exclude_none=True, exclude_unset=True),
         )
 
 
 class VideoReimportHistoryConfig(BaseModel):
     """Persisted processing-history config for a video re-import job."""
 
     model_config = ConfigDict(extra="forbid")
 
     kind: VideoReimportOperation = VIDEO_REIMPORT_OPERATION
     queue: str = Field(min_length=1)
     refresh_predictions: bool = True
     prediction_payload: JsonObject = Field(default_factory=_empty_json_object)
 
 
 class VideoReimportDispatchResult(BaseModel):
     """Queue or inline execution result returned by video re-import dispatch."""
 
     model_config = ConfigDict(extra="forbid")
 
     task_id: str
     mode: VideoReimportJobMode
     status: VideoReimportDispatchStatus
     operation: VideoReimportOperation = VIDEO_REIMPORT_HISTORY_KIND
     video_id: int = Field(ge=1)
     queue: str = Field(min_length=1)
     history_id: int | None = Field(default=None, ge=1)
     poll_url: str | None = None
     message: str | None = None
     reason: str | None = None
     prediction_refresh: JsonObject | None = None
 
     def to_dict(self) -> JsonObject:
         return cast(JsonObject, self.model_dump(mode="json", exclude_none=True))
 
 
 class VideoReimportPredictionRefreshPayload(BaseModel):
     """Fallback prediction-refresh status produced after inline re-import."""
 
     model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
 
     status: VideoReimportPredictionRefreshStatus
     queued: bool = False
     reason: str | None = None
     error: str | None = None
 
     def to_dict(self) -> JsonObject:
         return cast(JsonObject, self.model_dump(mode="json", exclude_none=True))
 
 
 class VideoReimportApiResponseData(TypedDict, total=False):
     error: str
     error_type: VideoReimportErrorType
     message: str
     status: VideoReimportApiStatus
     operation: VideoReimportOperation
     reason: str
     video_id: int
     uuid: str
     updated_in_place: bool
     task_id: str
     mode: str
     queue: str
     history_id: int
     poll_url: str
     frame_cleaning_applied: bool
     sensitive_meta_created: bool
     sensitive_meta_id: int | None
     reset_upload_jobs: int
     completed_upload_jobs: int
     prediction_refresh: JsonObject
 
 
 class VideoReimportApiResponsePayload(BaseModel):
     """Validated API response payload for video re-import endpoints."""
 
     model_config = ConfigDict(extra="allow", str_strip_whitespace=True)
 
     error: str | None = None
     error_type: VideoReimportErrorType | None = None
     message: str | None = None
     status: VideoReimportApiStatus | None = None
     operation: VideoReimportOperation | None = None
     reason: str | None = None
     video_id: int | None = Field(default=None, ge=1)
     uuid: str | None = None
     updated_in_place: bool | None = None
     task_id: str | None = None
     mode: VideoReimportJobMode | None = None
     queue: str | None = Field(default=None, min_length=1)
     history_id: int | None = Field(default=None, ge=1)
     poll_url: str | None = None
     frame_cleaning_applied: bool | None = None
     sensitive_meta_created: bool | None = None
     sensitive_meta_id: int | None = Field(default=None, ge=1)
     reset_upload_jobs: int | None = Field(default=None, ge=0)
     completed_upload_jobs: int | None = Field(default=None, ge=0)
     prediction_refresh: JsonObject | None = None
 
 
 def video_reimport_json_safe(value: object) -> VideoReimportJsonValue:
     if value is None or isinstance(value, (str, int, float, bool)):
         return value
     if isinstance(value, Mapping):
         mapping = cast(Mapping[object, object], value)
-        return {
-            str(key): video_reimport_json_safe(item) for key, item in mapping.items()
-        }
+        return cast(
+            VideoReimportJsonValue,
+            {str(key): video_reimport_json_safe(item) for key, item in mapping.items()},
+        )
     if isinstance(value, (list, tuple)):
         items = cast(list[object] | tuple[object, ...], value)
-        return [video_reimport_json_safe(item) for item in items]
+        return cast(
+            VideoReimportJsonValue,
+            [video_reimport_json_safe(item) for item in items],
+        )
     return str(value)
 
 
-def video_reimport_json_safe_dict(payload: object) -> JsonObject:
-    if not isinstance(payload, Mapping):
-        return {}
+def video_reimport_json_safe_dict(payload: Mapping[str, JsonValue]) -> JsonObject:
     mapping = cast(Mapping[object, object], payload)
-    return {str(key): video_reimport_json_safe(value) for key, value in mapping.items()}
+    return cast(
+        JsonObject,
+        {str(key): video_reimport_json_safe(value) for key, value in mapping.items()},
+    )
 
 
 def validate_video_reimport_request_payload(
-    payload: object,
+    payload: Mapping[str, JsonValue],
 ) -> VideoReimportRequestPayload:
     return VideoReimportRequestPayload.model_validate(
         video_reimport_json_safe_dict(payload)
     )
 
 
 def dump_video_reimport_request_payload(
     payload: VideoReimportRequestPayload,
 ) -> VideoReimportRequestData:
     return cast(VideoReimportRequestData, payload.to_payload_dict())
 
 
 def dump_video_reimport_api_response(
     payload: VideoReimportApiResponsePayload,
 ) -> VideoReimportApiResponseData:
     return cast(
         VideoReimportApiResponseData,
         payload.model_dump(mode="json", exclude_none=True),
     )
 
 
 __all__ = [
     "JsonObject",
     "JsonValue",
     "VIDEO_REIMPORT_HISTORY_KIND",
     "VIDEO_REIMPORT_OPERATION",
     "VideoReimportApiResponseData",
     "VideoReimportApiResponsePayload",
     "VideoReimportApiStatus",
     "VideoReimportDispatchResult",
     "VideoReimportDispatchStatus",
     "VideoReimportErrorType",
     "VideoReimportHistoryConfig",
     "VideoReimportJobMode",
     "VideoReimportJsonValue",
     "VideoReimportOperation",
     "VideoReimportPredictionRefreshPayload",
     "VideoReimportPredictionRefreshStatus",
     "VideoReimportRequestData",
     "VideoReimportRequestPayload",
     "VideoReimportStatus",
     "dump_video_reimport_api_response",
     "dump_video_reimport_request_payload",
     "validate_video_reimport_request_payload",
     "video_reimport_json_safe",
     "video_reimport_json_safe_dict",
 ]
diff --git lx_dtypes/models/contracts/video_segments.py lx_dtypes/models/contracts/video_segments.py
index ec81945..b7975a2 100644
--- lx_dtypes/models/contracts/video_segments.py
+++ lx_dtypes/models/contracts/video_segments.py
@@ -1,484 +1,497 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from typing import TypeAlias, cast
 
 from pydantic import (
     BaseModel,
     ConfigDict,
     Field,
     RootModel,
     ValidationError,
     field_validator,
     model_validator,
 )
 
 from .json_types import JsonObject
+from .json_types import JsonValue
 
 
 VideoSegmentsPayloadDict: TypeAlias = dict[str, list[tuple[int, int]]]
 
 
 class VideoSegmentsPayload(RootModel[VideoSegmentsPayloadDict]):
     model_config = ConfigDict(strict=True)
 
     @property
     def as_dict(self) -> VideoSegmentsPayloadDict:
         return self.root
 
 
 class SegmentAnnotationMetadataInput(BaseModel):
     model_config = ConfigDict(extra="ignore")
 
     segment_id: int | None = None
 
 
 class SegmentAnnotationInput(BaseModel):
     model_config = ConfigDict(extra="ignore")
 
     annotation_type: str = Field(alias="type")
     video_id: int = Field(gt=0)
     start_time: float = Field(ge=0)
     end_time: float = Field(ge=0)
     text: str = ""
     tags: list[str] = Field(default_factory=list)
     metadata: SegmentAnnotationMetadataInput = Field(
         default_factory=SegmentAnnotationMetadataInput
     )
 
     @model_validator(mode="after")
     def validate_segment_annotation(self) -> SegmentAnnotationInput:
         if self.annotation_type != "segment":
             raise ValueError("annotation type must be 'segment'")
         if self.end_time <= self.start_time:
             raise ValueError("end_time must be greater than start_time")
         return self
 
     def to_frame_range(self, fps: float) -> tuple[int, int]:
         return (
             int(round(self.start_time * fps)),
             int(round(self.end_time * fps)),
         )
 
 
-def validate_video_segments_payload(value: object) -> VideoSegmentsPayload:
+def validate_video_segments_payload(
+    value: VideoSegmentsPayloadDict | Mapping[str, list[tuple[int, int]]],
+) -> VideoSegmentsPayload:
     return VideoSegmentsPayload.model_validate(value)
 
 
 def parse_segment_annotation_input(
-    annotation: SegmentAnnotationInput | Mapping[str, object],
+    annotation: SegmentAnnotationInput | Mapping[str, JsonValue],
 ) -> SegmentAnnotationInput | None:
     if isinstance(annotation, SegmentAnnotationInput):
         return annotation
 
     try:
         return SegmentAnnotationInput.model_validate(annotation)
     except ValidationError:
         return None
 
 
-def _blank_to_none(value: object) -> object:
+def _blank_to_none(value: str | int | float | bool | None) -> str | None:
     if isinstance(value, str):
         stripped = value.strip()
         return stripped or None
-    return value
+    if value is None:
+        return None
+    return str(value).strip() or None
 
 
-def _payload_dict(payload: Mapping[str, object]) -> dict[str, object]:
+def _payload_dict(payload: Mapping[str, JsonValue]) -> dict[str, JsonValue]:
     return dict(payload)
 
 
 def _empty_segment_id_list() -> list[int]:
     return []
 
 
 def _empty_bulk_validation_item_list() -> list[SegmentBulkValidationItem]:
     return []
 
 
 class SegmentCrudPayload(BaseModel):
     model_config = ConfigDict(
         extra="allow",
         populate_by_name=True,
         str_strip_whitespace=True,
     )
 
     ai_dataset_id: int | None = Field(default=None, ge=1)
     video_id: int | None = Field(default=None, ge=1)
     video_file: int | None = Field(default=None, ge=1)
     label_id: int | None = Field(default=None, ge=1)
     label: int | None = Field(default=None, ge=1)
     label_name: str | None = None
     start_frame_number: int | None = Field(default=None, ge=0)
     end_frame_number: int | None = Field(default=None, ge=0)
     start_time: float | None = Field(default=None, ge=0)
     end_time: float | None = Field(default=None, ge=0)
     export_segment: bool | None = None
 
     @field_validator("label_name", mode="before")
     @classmethod
-    def _normalize_label_name(cls, value: object) -> object:
+    def _normalize_label_name(cls, value: str | int | float | bool | None) -> str | None:
         return _blank_to_none(value)
 
     @model_validator(mode="after")
     def _validate_segment_bounds(self) -> SegmentCrudPayload:
         if self.start_frame_number is not None and self.end_frame_number is not None:
             if self.end_frame_number <= self.start_frame_number:
                 raise ValueError(
                     "end_frame_number must be greater than start_frame_number"
                 )
 
         if self.start_time is not None and self.end_time is not None:
             SegmentAnnotationInput.model_validate(
                 {
                     "type": "segment",
                     "video_id": self.video_id or self.video_file or 1,
                     "start_time": self.start_time,
                     "end_time": self.end_time,
                     "metadata": {},
                 }
             )
         return self
 
     def serializer_payload(self, *, video_id: int | None = None) -> JsonObject:
         payload = cast(
             JsonObject,
             self.model_dump(
                 mode="python",
                 exclude={"ai_dataset_id"},
                 exclude_unset=True,
             ),
         )
         if payload.get("video_file") is not None and payload.get("video_id") is None:
             payload["video_id"] = payload["video_file"]
         payload.pop("video_file", None)
         if payload.get("label") is not None and payload.get("label_id") is None:
             payload["label_id"] = payload["label"]
         payload.pop("label", None)
         if video_id is not None:
             payload["video_id"] = video_id
         return payload
 
 
 class SegmentListQuery(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     video_id: int | None = Field(default=None, ge=1)
     label_id: int | None = Field(default=None, ge=1)
     label: str | None = None
     source_kind: str | None = None
     include_annotation_payload: bool = False
 
     @field_validator("label", "source_kind", mode="before")
     @classmethod
-    def _normalize_optional_text(cls, value: object) -> object:
+    def _normalize_optional_text(cls, value: str | int | float | bool | None) -> str | None:
         return _blank_to_none(value)
 
 
 class SegmentBlackenOutsidePayload(BaseModel):
     model_config = ConfigDict(extra="ignore")
 
     only_validated: bool = False
 
 
 class SegmentValidationPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     is_validated: bool = True
     information_source_name: str = "manual_annotation"
     annotator: str | None = None
     start_time: float | None = Field(default=None, ge=0)
     end_time: float | None = Field(default=None, ge=0)
 
     @field_validator("information_source_name", mode="before")
     @classmethod
-    def _default_information_source(cls, value: object) -> str:
+    def _default_information_source(cls, value: str | int | float | bool | None) -> str:
         normalized = _blank_to_none(value)
         return str(normalized or "manual_annotation")
 
     @field_validator("annotator", mode="before")
     @classmethod
-    def _normalize_annotator(cls, value: object) -> object:
+    def _normalize_annotator(cls, value: str | int | float | bool | None) -> str | None:
         return _blank_to_none(value)
 
     @model_validator(mode="after")
     def _validate_optional_timing_pair(self) -> SegmentValidationPayload:
         has_start = self.start_time is not None
         has_end = self.end_time is not None
         if has_start != has_end:
             raise ValueError("start_time and end_time must be provided together")
         if has_start and has_end:
             SegmentAnnotationInput.model_validate(
                 {
                     "type": "segment",
                     "video_id": 1,
                     "start_time": self.start_time,
                     "end_time": self.end_time,
                     "metadata": {},
                 }
             )
         return self
 
     def to_annotation_input(self, *, video_id: int) -> SegmentAnnotationInput | None:
         if self.start_time is None or self.end_time is None:
             return None
         return SegmentAnnotationInput.model_validate(
             {
                 "type": "segment",
                 "video_id": video_id,
                 "start_time": self.start_time,
                 "end_time": self.end_time,
                 "metadata": {},
             }
         )
 
 
 class SegmentBulkValidationItem(BaseModel):
     model_config = ConfigDict(extra="ignore")
 
     id: int = Field(gt=0)
     start_time: float | None = Field(default=None, ge=0)
     end_time: float | None = Field(default=None, ge=0)
 
     @model_validator(mode="after")
     def _validate_optional_timing_pair(self) -> SegmentBulkValidationItem:
         has_start = self.start_time is not None
         has_end = self.end_time is not None
         if has_start != has_end:
             raise ValueError("start_time and end_time must be provided together")
         if has_start and has_end:
             SegmentAnnotationInput.model_validate(
                 {
                     "type": "segment",
                     "video_id": 1,
                     "start_time": self.start_time,
                     "end_time": self.end_time,
                     "metadata": {},
                 }
             )
         return self
 
     def to_annotation_input(self, *, video_id: int) -> SegmentAnnotationInput | None:
         if self.start_time is None or self.end_time is None:
             return None
         return SegmentAnnotationInput.model_validate(
             {
                 "type": "segment",
                 "video_id": video_id,
                 "start_time": self.start_time,
                 "end_time": self.end_time,
                 "metadata": {},
             }
         )
 
 
 class SegmentBulkValidationPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     segment_ids: list[int] = Field(default_factory=_empty_segment_id_list)
     segments: list[SegmentBulkValidationItem] = Field(
         default_factory=_empty_bulk_validation_item_list
     )
     is_validated: bool = True
     notes: str = ""
     information_source_name: str = "manual_annotation"
     annotator: str | None = None
 
     @field_validator("segment_ids", mode="before")
     @classmethod
-    def _normalize_segment_ids(cls, value: object) -> object:
+    def _normalize_segment_ids(
+        cls, value: list[int] | tuple[int, ...] | int | str | None
+    ) -> list[int] | None:
         if value is None:
             return []
         if isinstance(value, list):
-            return [item for item in cast(list[object], value) if item is not None]
-        return [value]
+            return [cast(int, item) for item in cast(list[object], value) if item is not None]
+        return [cast(int, value)]
 
     @field_validator("information_source_name", mode="before")
     @classmethod
-    def _default_information_source(cls, value: object) -> str:
+    def _default_information_source(
+        cls, value: str | int | float | bool | None
+    ) -> str:
         normalized = _blank_to_none(value)
         return str(normalized or "manual_annotation")
 
     @field_validator("annotator", mode="before")
     @classmethod
-    def _normalize_annotator(cls, value: object) -> object:
+    def _normalize_annotator(cls, value: str | int | float | bool | None) -> str | None:
         return _blank_to_none(value)
 
     @field_validator("notes", mode="before")
     @classmethod
-    def _default_notes(cls, value: object) -> str:
+    def _default_notes(cls, value: str | int | float | bool | None) -> str:
         return str(value or "")
 
     @model_validator(mode="after")
     def _validate_segment_ids(self) -> SegmentBulkValidationPayload:
         if any(segment_id <= 0 for segment_id in self.segment_ids):
             raise ValueError("segment_ids must contain positive integers")
         return self
 
     @property
     def timing_by_segment_id(self) -> dict[int, SegmentBulkValidationItem]:
         return {item.id: item for item in self.segments}
 
 
 class SegmentPredictionImportItem(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     label_name: str | None = None
     label: str | None = None
     start_time: float = Field(ge=0)
     end_time: float = Field(ge=0)
     export_segment: bool = False
 
     @field_validator("label_name", "label", mode="before")
     @classmethod
-    def _normalize_label_text(cls, value: object) -> object:
+    def _normalize_label_text(cls, value: str | int | float | bool | None) -> str | None:
         return _blank_to_none(value)
 
     @model_validator(mode="after")
     def _validate_import_item(self) -> SegmentPredictionImportItem:
         if not (self.label_name or self.label):
             raise ValueError("label_name or label is required")
         SegmentAnnotationInput.model_validate(
             {
                 "type": "segment",
                 "video_id": 1,
                 "start_time": self.start_time,
                 "end_time": self.end_time,
                 "metadata": {},
             }
         )
         return self
 
     def serializer_payload(self, *, video_id: int) -> JsonObject:
         return {
             "video_id": video_id,
             "label_name": self.label_name or self.label,
             "start_time": self.start_time,
             "end_time": self.end_time,
             "export_segment": self.export_segment,
         }
 
 
 class SegmentPredictionImportPayload(BaseModel):
     model_config = ConfigDict(extra="ignore")
 
     segments: list[SegmentPredictionImportItem] = Field(min_length=1)
     replace_existing: bool = True
 
 
 class SegmentAnnotationEnsurePayload(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     video_ids: list[int] | None = None
     segment_ids: list[int] | None = None
     information_source_name: str = "manual_annotation"
 
     @field_validator("video_ids", "segment_ids", mode="before")
     @classmethod
-    def _normalize_optional_int_list(cls, value: object) -> object:
+    def _normalize_optional_int_list(
+        cls, value: list[int] | tuple[int, ...] | int | str | None
+    ) -> list[int] | None:
         if value is None:
             return None
         if isinstance(value, list):
-            return [item for item in cast(list[object], value) if item is not None]
-        return [value]
+            return [cast(int, item) for item in cast(list[object], value) if item is not None]
+        return [cast(int, value)]
 
     @field_validator("information_source_name", mode="before")
     @classmethod
-    def _default_information_source(cls, value: object) -> str:
+    def _default_information_source(
+        cls, value: str | int | float | bool | None
+    ) -> str:
         normalized = _blank_to_none(value)
         return str(normalized or "manual_annotation")
 
     @model_validator(mode="after")
     def _validate_ids(self) -> SegmentAnnotationEnsurePayload:
         for field_name in ("video_ids", "segment_ids"):
             values = getattr(self, field_name)
             if values is not None and any(value <= 0 for value in values):
                 raise ValueError(f"{field_name} must contain positive integers")
         return self
 
 
 class SegmentValidationStatusPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
 
     label_name: str | None = None
 
     @field_validator("label_name", mode="before")
     @classmethod
-    def _normalize_label_name(cls, value: object) -> object:
+    def _normalize_label_name(cls, value: str | int | float | bool | None) -> str | None:
         return _blank_to_none(value)
 
 
-def validate_segment_crud_payload(payload: Mapping[str, object]) -> SegmentCrudPayload:
+def validate_segment_crud_payload(payload: Mapping[str, JsonValue]) -> SegmentCrudPayload:
     return SegmentCrudPayload.model_validate(_payload_dict(payload))
 
 
-def validate_segment_list_query(payload: Mapping[str, object]) -> SegmentListQuery:
+def validate_segment_list_query(payload: Mapping[str, JsonValue]) -> SegmentListQuery:
     return SegmentListQuery.model_validate(_payload_dict(payload))
 
 
 def validate_segment_blacken_outside_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> SegmentBlackenOutsidePayload:
     return SegmentBlackenOutsidePayload.model_validate(_payload_dict(payload))
 
 
 def validate_segment_validation_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> SegmentValidationPayload:
     return SegmentValidationPayload.model_validate(_payload_dict(payload))
 
 
 def validate_segment_bulk_validation_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> SegmentBulkValidationPayload:
     return SegmentBulkValidationPayload.model_validate(_payload_dict(payload))
 
 
 def validate_segment_prediction_import_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> SegmentPredictionImportPayload:
     return SegmentPredictionImportPayload.model_validate(_payload_dict(payload))
 
 
 def validate_segment_annotation_ensure_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
     *,
     default_information_source_name: str,
 ) -> SegmentAnnotationEnsurePayload:
     data = _payload_dict(payload)
     data.setdefault("information_source_name", default_information_source_name)
     return SegmentAnnotationEnsurePayload.model_validate(data)
 
 
 def validate_segment_validation_status_payload(
-    payload: Mapping[str, object],
+    payload: Mapping[str, JsonValue],
 ) -> SegmentValidationStatusPayload:
     return SegmentValidationStatusPayload.model_validate(_payload_dict(payload))
 
 
 __all__ = [
     "VideoSegmentsPayload",
     "VideoSegmentsPayloadDict",
     "SegmentAnnotationEnsurePayload",
     "SegmentAnnotationInput",
     "SegmentAnnotationMetadataInput",
     "SegmentBlackenOutsidePayload",
     "SegmentBulkValidationItem",
     "SegmentBulkValidationPayload",
     "SegmentCrudPayload",
     "SegmentListQuery",
     "SegmentPredictionImportItem",
     "SegmentPredictionImportPayload",
     "SegmentValidationPayload",
     "SegmentValidationStatusPayload",
     "parse_segment_annotation_input",
     "validate_segment_annotation_ensure_payload",
     "validate_segment_blacken_outside_payload",
     "validate_segment_bulk_validation_payload",
     "validate_segment_crud_payload",
     "validate_segment_list_query",
     "validate_segment_prediction_import_payload",
     "validate_segment_validation_payload",
     "validate_video_segments_payload",
     "validate_segment_validation_status_payload",
 ]
diff --git lx_dtypes/models/contracts/video_state.py lx_dtypes/models/contracts/video_state.py
index 99bde14..db25f24 100644
--- lx_dtypes/models/contracts/video_state.py
+++ lx_dtypes/models/contracts/video_state.py
@@ -1,18 +1,19 @@
 from __future__ import annotations
 
+from collections.abc import Mapping
 from pydantic import BaseModel, ConfigDict, Field
 
 
 class VideoFrameStateContract(BaseModel):
     model_config = ConfigDict(extra="forbid", strict=True, from_attributes=True)
 
     frame_count: int | None = Field(default=None, ge=0)
 
 
 def parse_video_frame_state_payload(
-    payload: object,
+    payload: Mapping[str, int | float | str | bool | None] | None,
 ) -> VideoFrameStateContract:
     return VideoFrameStateContract.model_validate(payload or {})
 
 
 __all__ = ["VideoFrameStateContract", "parse_video_frame_state_payload"]
diff --git lx_dtypes/models/contracts/video_temporal_inference.py lx_dtypes/models/contracts/video_temporal_inference.py
index 762a267..1ba2ce8 100644
--- lx_dtypes/models/contracts/video_temporal_inference.py
+++ lx_dtypes/models/contracts/video_temporal_inference.py
@@ -1,92 +1,92 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from dataclasses import asdict, dataclass
 
 from pydantic import BaseModel, ConfigDict, Field
 
-from .json_types import JsonObject
+from .json_types import JsonObject, JsonValue
 
 
 @dataclass(frozen=True)
 class TemporalInferenceDispatchResult:
     task_id: str
     mode: str
     status: str
     video_id: int
     model_meta_id: int
     queue: str
     history_id: int | None = None
     deleted_prediction_segments: int | None = None
     prediction_segments_count: int | None = None
     reason: str | None = None
     message: str | None = None
     blocked_by_history_id: int | None = None
 
-    def to_dict(self) -> dict[str, object]:
+    def to_dict(self) -> dict[str, JsonValue]:
         return asdict(self)
 
 
 class TemporalInferenceHistoryResultPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", strict=False)
 
     backend: str | None = None
     device: str | None = None
     duration_ms: float | None = None
     provenance: JsonObject = Field(default_factory=dict)
     score_frame_count: int | None = None
     score_label_count: int | None = None
     score_frame_numbers_present: bool | None = None
     score_timestamps_present: bool | None = None
     frame_source_mode: str | None = None
     requested_frame_source_mode: str | None = None
     resolved_frame_source_mode: str | None = None
     source_video_kind: str | None = None
     temporal_segment_count: int | None = None
     materialized_segment_count: int | None = None
     created_segment_count: int | None = None
     deleted_prediction_segments: int | None = None
     score_vectors_stored: bool | None = None
 
 
 class TemporalInferenceHistoryConfigPayload(BaseModel):
     model_config = ConfigDict(extra="ignore", strict=False)
 
     kind: str | None = None
     model_meta_id: int | None = None
     replace_prediction_segments: bool = True
     delete_frames_after: bool = True
     ocr_frame_fraction: float | None = None
     ocr_cap: int = 10
     temporal_options: JsonObject = Field(default_factory=dict)
     raw_temporal_options: JsonObject = Field(default_factory=dict)
     queue: str = ""
     frame_source_mode: str | None = None
     requested_frame_source_mode: str | None = None
     resolved_frame_source_mode: str | None = None
     test_run: bool = False
     n_test_frames: int = 10
     deferred_reason: str | None = None
     blocked_by_history_id: int | None = None
     result: TemporalInferenceHistoryResultPayload | None = None
 
 
 def parse_temporal_inference_history_config_payload(
-    payload: Mapping[str, object] | None,
+    payload: Mapping[str, JsonValue] | None,
 ) -> TemporalInferenceHistoryConfigPayload:
     return TemporalInferenceHistoryConfigPayload.model_validate(payload or {})
 
 
 def parse_temporal_inference_history_result_payload(
-    payload: Mapping[str, object] | None,
+    payload: Mapping[str, JsonValue] | None,
 ) -> TemporalInferenceHistoryResultPayload:
     return TemporalInferenceHistoryResultPayload.model_validate(payload or {})
 
 
 __all__ = [
     "TemporalInferenceDispatchResult",
     "TemporalInferenceHistoryConfigPayload",
     "TemporalInferenceHistoryResultPayload",
     "parse_temporal_inference_history_config_payload",
     "parse_temporal_inference_history_result_payload",
 ]
diff --git lx_dtypes/models/contracts/video_text_metadata.py lx_dtypes/models/contracts/video_text_metadata.py
index ba1e02a..3a7ca35 100644
--- lx_dtypes/models/contracts/video_text_metadata.py
+++ lx_dtypes/models/contracts/video_text_metadata.py
@@ -1,33 +1,35 @@
 from __future__ import annotations
 
 from collections.abc import Mapping
 from typing import cast
 
 from pydantic import ConfigDict, RootModel, field_validator
 
 from .json_types import JsonNull, JsonValue
 
 
 type VideoTextMetaValue = (
     JsonValue | JsonNull | list["VideoTextMetaValue"] | dict[str, "VideoTextMetaValue"]
 )
 
 
 class VideoTextMetaPayload(RootModel[dict[str, VideoTextMetaValue]]):
     model_config = ConfigDict(frozen=True, strict=True)
 
     @field_validator("root", mode="before")
     @classmethod
-    def normalize_root(cls, value: object) -> dict[str, VideoTextMetaValue]:
+    def normalize_root(
+        cls, value: Mapping[str, VideoTextMetaValue]
+    ) -> dict[str, VideoTextMetaValue]:
         if not isinstance(value, Mapping):
             raise ValueError("video text metadata must be a JSON object")
-        mapping = cast(Mapping[object, object], value)
+        mapping = cast(Mapping[object, VideoTextMetaValue], value)
         return {
             str(key): cast(VideoTextMetaValue, item) for key, item in mapping.items()
         }
 
     def to_dict(self) -> dict[str, VideoTextMetaValue]:
         return cast(dict[str, VideoTextMetaValue], self.model_dump(mode="python"))
 
 
 __all__ = ["VideoTextMetaPayload", "VideoTextMetaValue"]
diff --git lx_dtypes/models/interface/Ledger.py lx_dtypes/models/interface/Ledger.py
index 65b9b1a..09125e1 100644
--- lx_dtypes/models/interface/Ledger.py
+++ lx_dtypes/models/interface/Ledger.py
@@ -1,308 +1,325 @@
-from typing import Dict, List, Tuple, TypedDict
+from typing import Dict, List, NotRequired, Tuple, TypedDict
 
 from pydantic import Field
 
 # from lx_dtypes.factories.typed_dicts
 from lx_dtypes.models.base.app_base_model.ddict.AppBaseModelUUIDTagsDataDict import (
     AppBaseModelUUIDTagsDataDict,
 )
 from lx_dtypes.models.base.app_base_model.pydantic.AppBaseModelUUIDTags import (
     AppBaseModelUUIDTags,
 )
 from lx_dtypes.models.ledger.case import Case, CaseDataDict
 from lx_dtypes.models.ledger.center import Center, CenterDataDict
 from lx_dtypes.models.ledger.examiner.DataDict import ExaminerDataDict
 from lx_dtypes.models.ledger.examiner.Pydantic import Examiner
+from lx_dtypes.models.ledger.report import Report, ReportDataDict
 from lx_dtypes.models.ledger.p_examination import PExamination, PExaminationDataDict
 from lx_dtypes.models.ledger.p_examination.DataDict import (
     SerializedPExaminationDataDict,
 )
 from lx_dtypes.models.ledger.p_finding.DataDict import (
     SerializedPFindingDataDict,
 )
 from lx_dtypes.models.ledger.p_finding_classification_choice.DataDict import (
     SerializedPFindingClassificationChoiceDataDict,
 )
 from lx_dtypes.models.ledger.p_finding_classification_choice_descriptor.DataDict import (
     PFindingClassificationChoiceDescriptorDataDict,
 )
 from lx_dtypes.models.ledger.p_finding_classifications.DataDict import (
     SerializedPFindingClassificationsDataDict,
 )
 from lx_dtypes.models.ledger.p_indication.DataDict import PIndicationDataDict
 from lx_dtypes.models.ledger.p_indication_classification.DataDict import (
     SerializedPIndicationClassificationDataDict,
 )
 from lx_dtypes.models.ledger.p_indication_classification_descriptor.DataDict import (
     PIndicationClassificationDescriptorDataDict,
 )
 from lx_dtypes.models.ledger.p_intervention.DataDict import PFindingInterventionDataDict
 from lx_dtypes.models.ledger.p_interventions.DataDict import (
     SerializedPFindingInterventionsDataDict,
 )
 from lx_dtypes.models.ledger.p_video import (
     PatientVideoFile,
     PatientVideoFileDataDict,
     # RawPatientVideoFileDataDict,
     # RawVideoFile,
 )
 from lx_dtypes.models.ledger.patient import Patient, PatientDataDict
+from lx_dtypes.models.ledger.video_file import VideoFile, VideoFileDataDict
 
 
 class LedgerRecordList(TypedDict):
+    reports: NotRequired[List[ReportDataDict]]
     patients: List[PatientDataDict]
     p_examinations: List[SerializedPExaminationDataDict]
     centers: List[CenterDataDict]
     examiners: List[ExaminerDataDict]
     p_findings: List[SerializedPFindingDataDict]
     p_indications: List[PIndicationDataDict]
     p_indication_classifications: List[SerializedPIndicationClassificationDataDict]
     p_indication_classification_descriptors: List[
         PIndicationClassificationDescriptorDataDict
     ]
     p_finding_classifications: List[SerializedPFindingClassificationsDataDict]
     p_finding_classification_choices: List[
         SerializedPFindingClassificationChoiceDataDict
     ]
     p_finding_classification_choice_descriptors: List[
         PFindingClassificationChoiceDescriptorDataDict
     ]
     p_finding_interventions: List[SerializedPFindingInterventionsDataDict]
     p_finding_intervention: List[PFindingInterventionDataDict]
+    video_files: NotRequired[List[VideoFileDataDict]]
     p_videos: List[PatientVideoFileDataDict]
     # p_raw_videos: List[RawPatientVideoFileDataDict]
 
 
 class LedgerDataDict(AppBaseModelUUIDTagsDataDict):
     cases: Dict[str, CaseDataDict]
+    reports: NotRequired[Dict[str, ReportDataDict]]
+    video_files: NotRequired[Dict[str, VideoFileDataDict]]
     patient_examinations: Dict[str, PExaminationDataDict]
     patients: Dict[str, PatientDataDict]
     centers: Dict[str, CenterDataDict]
     patient_videos: Dict[str, PatientVideoFileDataDict]
     # raw_patient_videos: Dict[str, RawPatientVideoFileDataDict]
 
 
 class Ledger(AppBaseModelUUIDTags):
     cases: Dict[str, Case] = Field(default_factory=dict)
+    reports: Dict[str, Report] = Field(default_factory=dict)
     patient_examinations: Dict[str, PExamination] = Field(default_factory=dict)
     patients: Dict[str, Patient] = Field(default_factory=dict)
     centers: Dict[str, Center] = Field(default_factory=dict)
     examiners: Dict[str, Examiner] = Field(default_factory=dict)
+    video_files: Dict[str, VideoFile] = Field(default_factory=dict)
     patient_videos: Dict[str, PatientVideoFile] = Field(default_factory=dict)
     # raw_patient_videos: Dict[str, RawVideoFile] = Field(default_factory=dict)
     # patient_images: Dict[str, PFile] = Field(default_factory=dict)
     # patient_pdfs: Dict[str, PFile] = Field(default_factory=dict)
     # video_segment_annotations
     # image_annotations
 
     def patient_exists(self, patient_uuid: str) -> bool:
         """
         Check whether a patient with the given UUID exists in the ledger.
 
         Returns:
             `true` if a patient with the given UUID exists, `false` otherwise.
         """
         return patient_uuid in self.patients
 
     def case_exists(self, case_uuid: str) -> bool:
         """Return whether a transient case with this UUID is present."""
         return case_uuid in self.cases
 
     def p_examination_exists(self, examination_uuid: str) -> bool:
         """
         Check whether a patient examination with the given UUID exists in the ledger.
 
         Returns:
             `True` if an examination with the given UUID is present in `self.patient_examinations`, `False` otherwise.
         """
         return examination_uuid in self.patient_examinations
 
     def export_patient_examination_record_list(
         self,
     ) -> Tuple[
         List[SerializedPExaminationDataDict],
         List[SerializedPFindingDataDict],
         List[PIndicationDataDict],
         List[SerializedPIndicationClassificationDataDict],
         List[PIndicationClassificationDescriptorDataDict],
         List[SerializedPFindingClassificationsDataDict],
         List[SerializedPFindingClassificationChoiceDataDict],
         List[PFindingClassificationChoiceDescriptorDataDict],
         List[SerializedPFindingInterventionsDataDict],
         List[PFindingInterventionDataDict],
     ]:
         """
         Collects and serializes all patient-examination-related records into eight separate lists.
 
         Returns:
             Tuple containing, in order:
             - p_examination_dicts (List[SerializedPExaminationDataDict]): Serialized patient examination records.
             - p_finding_dicts (List[SerializedPFindingDataDict]): Serialized findings associated with examinations.
             - p_indication_dicts (List[PIndicationDataDict]): Serialized indications associated with examinations.
             - p_finding_classifications_dicts (List[SerializedPFindingClassificationsDataDict]): Serialized finding-classification records.
             - p_finding_classification_choice_dicts (List[SerializedPFindingClassificationChoiceDataDict]): Serialized classification choice records.
             - p_finding_classification_choice_descriptor_dicts (List[PFindingClassificationChoiceDescriptorDataDict]): Descriptor dictionaries for classification choices.
             - p_finding_interventions_dicts (List[SerializedPFindingInterventionsDataDict]): Serialized intervention-group records for findings.
             - p_finding_intervention_dicts (List[PFindingInterventionDataDict]): Serialized individual intervention records.
         """
         p_examination_dicts: List[SerializedPExaminationDataDict] = []
         p_finding_dicts: List[SerializedPFindingDataDict] = []
         p_indication_dicts: List[PIndicationDataDict] = []
         p_indication_classification_dicts: List[
             SerializedPIndicationClassificationDataDict
         ] = []
         p_indication_classification_descriptor_dicts: List[
             PIndicationClassificationDescriptorDataDict
         ] = []
         p_finding_classifications_dicts: List[
             SerializedPFindingClassificationsDataDict
         ] = []
         p_finding_classification_choice_dicts: List[
             SerializedPFindingClassificationChoiceDataDict
         ] = []
         p_finding_classification_choice_descriptor_dicts: List[
             PFindingClassificationChoiceDescriptorDataDict
         ] = []
         p_finding_interventions_dicts: List[
             SerializedPFindingInterventionsDataDict
         ] = []
         p_finding_intervention_dicts: List[PFindingInterventionDataDict] = []
 
         for p_examination in self.patient_examinations.values():
             # 1. Export PExamination
             p_examination_dicts.append(p_examination.serialized_ddict)
 
             # 2. Export PIndication
             for p_indication in p_examination.patient_indications:
                 p_indication_dicts.append(p_indication.serialized_ddict)
                 for (
                     p_indication_classification
                 ) in p_indication.patient_indication_classifications:
                     p_indication_classification_dicts.append(
                         p_indication_classification.serialized_ddict
                     )
                     for p_indication_classification_descriptor in p_indication_classification.patient_indication_classification_descriptors:
                         p_indication_classification_descriptor_dicts.append(
                             p_indication_classification_descriptor.ddict
                         )
 
             # 3. Export PFinding and nested classifications
             for p_finding in p_examination.patient_findings:
                 p_finding_dicts.append(p_finding.serialized_ddict)
 
                 # 4. Export PFindingClassifications and nested choices
                 for (
                     p_finding_classifications
                 ) in p_finding.patient_finding_classifications:
                     p_finding_classifications_dicts.append(
                         p_finding_classifications.serialized_ddict
                     )
 
                     # 5. Export PFindingClassificationChoice and nested descriptors
                     for p_finding_classification_choice in (
                         p_finding_classifications.patient_finding_classification_choices
                     ):
                         p_finding_classification_choice_dicts.append(
                             p_finding_classification_choice.serialized_ddict
                         )
 
                         # 6. Export PFindingClassificationChoiceDescriptor
                         for p_finding_classification_choice_descriptor in p_finding_classification_choice.patient_finding_classification_choice_descriptors:
                             p_finding_classification_choice_descriptor_dicts.append(
                                 p_finding_classification_choice_descriptor.ddict
                             )
                 # 7. Export PFindingInterventions and nested PFindingIntervention
                 for p_finding_interventions in p_finding.patient_finding_interventions:
                     p_finding_interventions_dicts.append(
                         p_finding_interventions.serialized_ddict
                     )
 
                     # 8. Export PFindingIntervention
                     for (
                         p_finding_intervention
                     ) in p_finding_interventions.patient_finding_interventions:
                         p_finding_intervention_dicts.append(
                             p_finding_intervention.serialized_ddict
                         )
 
         return (
             p_examination_dicts,
             p_finding_dicts,
             p_indication_dicts,
             p_indication_classification_dicts,
             p_indication_classification_descriptor_dicts,
             p_finding_classifications_dicts,
             p_finding_classification_choice_dicts,
             p_finding_classification_choice_descriptor_dicts,
             p_finding_interventions_dicts,
             p_finding_intervention_dicts,
         )
 
     def export_record_lists(self) -> LedgerRecordList:
         """
         Collects serialized representations of all ledger entities and returns them as a LedgerRecordList suitable for export.
 
         The returned record list contains flattened lists for patients, patient examinations, centers, examiners, findings, indications, finding classifications, classification choices, classification choice descriptors, finding interventions, and individual finding intervention records.
 
         Returns:
             LedgerRecordList: A TypedDict with these keys populated:
                 - patients: List of patient data dicts.
                 - p_examinations: List of serialized patient examination data dicts.
                 - centers: List of center data dicts.
                 - examiners: List of examiner data dicts.
                 - p_findings: List of serialized finding data dicts.
                 - p_indications: List of indication data dicts.
                 - p_finding_classifications: List of serialized finding classifications data dicts.
                 - p_finding_classification_choices: List of serialized classification choice data dicts.
                 - p_finding_classification_choice_descriptors: List of classification choice descriptor data dicts.
                 - p_finding_interventions: List of serialized finding interventions data dicts.
                 - p_finding_intervention: List of individual finding intervention data dicts.
+                - video_files: List of serialized technical VideoFile records.
                 - patient_video_file_dicts (List[PatientVideoFileDataDict]): Serialized patient video file records.
                 - raw_patient_video_file_dicts (List[RawPatientVideoFileDataDict]): Serialized raw patient video file records.
         """
+        report_dicts: List[ReportDataDict] = [
+            r.serialized_ddict for r in self.reports.values()
+        ]
         patient_dicts: List[PatientDataDict] = [
             r.serialized_ddict for r in self.patients.values()
         ]
         examiner_dicts: List[ExaminerDataDict] = [
             r.serialized_ddict for r in self.examiners.values()
         ]
         center_dicts: List[CenterDataDict] = [
             r.serialized_ddict for r in self.centers.values()
         ]
 
+        video_file_dicts: List[VideoFileDataDict] = [
+            r.serialized_ddict for r in self.video_files.values()
+        ]
         video_dicts = [r.serialized_ddict for r in self.patient_videos.values()]
         # raw_video_dicts = [r.serialized_ddict for r in self.raw_patient_videos.values()]
 
         (
             p_examination_dicts,
             p_finding_dicts,
             p_indication_dicts,
             p_indication_classification_dicts,
             p_indication_classification_descriptor_dicts,
             p_finding_classifications_dicts,
             p_finding_classification_choice_dicts,
             p_finding_classification_choice_descriptor_dicts,
             p_finding_interventions_dicts,
             p_finding_intervention_dicts,
         ) = self.export_patient_examination_record_list()
 
         record_list: LedgerRecordList = LedgerRecordList(
+            reports=report_dicts,
             patients=patient_dicts,
             p_examinations=p_examination_dicts,
             centers=center_dicts,
             examiners=examiner_dicts,
             p_findings=p_finding_dicts,
             p_indications=p_indication_dicts,
             p_indication_classifications=p_indication_classification_dicts,
             p_indication_classification_descriptors=(
                 p_indication_classification_descriptor_dicts
             ),
             p_finding_classifications=p_finding_classifications_dicts,
             p_finding_classification_choices=p_finding_classification_choice_dicts,
             p_finding_classification_choice_descriptors=p_finding_classification_choice_descriptor_dicts,
             p_finding_interventions=p_finding_interventions_dicts,
             p_finding_intervention=p_finding_intervention_dicts,
+            video_files=video_file_dicts,
             p_videos=video_dicts,
             # p_raw_videos=raw_video_dicts,
         )
         return record_list
diff --git lx_dtypes/models/ledger/case/DataDict.py lx_dtypes/models/ledger/case/DataDict.py
index 005d7ba..c5f56f5 100644
--- lx_dtypes/models/ledger/case/DataDict.py
+++ lx_dtypes/models/ledger/case/DataDict.py
@@ -1,24 +1,26 @@
 from typing import TYPE_CHECKING, List, Optional
 
 from lx_dtypes.models.base.app_base_model.ddict.LedgerBaseModelDataDict import (
     LedgerBaseModelDataDict,
 )
 
 if TYPE_CHECKING:
     from lx_dtypes.models.ledger.p_examination.DataDict import PExaminationDataDict
 
 
 class CaseDataDict(LedgerBaseModelDataDict):
     case_id: str
     patient: str
     admission_date: str
     leave_date: Optional[str]
     patient_examinations: List["PExaminationDataDict"]
+    report_ids: List[str]
 
 
 class SerializedCaseDataDict(LedgerBaseModelDataDict):
     case_id: str
     patient: str
     admission_date: str
     leave_date: Optional[str]
     patient_examinations: str
+    report_ids: str
diff --git lx_dtypes/models/ledger/case/Pydantic.py lx_dtypes/models/ledger/case/Pydantic.py
index ac2cd15..222e237 100644
--- lx_dtypes/models/ledger/case/Pydantic.py
+++ lx_dtypes/models/ledger/case/Pydantic.py
@@ -1,117 +1,151 @@
 from __future__ import annotations
 
 import datetime
 from typing import Any
 
 from pydantic import (
     AliasChoices,
     AwareDatetime,
     Field,
     field_validator,
     model_validator,
 )
 
 from lx_dtypes.models.base.app_base_model.pydantic.LedgerBaseModel import (
     LedgerBaseModel,
 )
 from lx_dtypes.models.ledger.p_examination.Pydantic import PExamination
 from lx_dtypes.names import CASE_MODEL_LIST_TYPE_FIELDS, CASE_MODEL_NESTED_FIELDS
 
 from .DataDict import CaseDataDict, SerializedCaseDataDict
 
 
 def _as_aware_datetime(value: Any) -> Any:
     """Normalize date-like case boundaries to timezone-aware datetimes."""
     if isinstance(value, str):
         try:
             value = datetime.datetime.fromisoformat(value)
         except ValueError:
             return value
     if isinstance(value, datetime.datetime):
         return (
             value
             if value.tzinfo is not None
             else value.replace(tzinfo=datetime.timezone.utc)
         )
     if isinstance(value, datetime.date):
         return datetime.datetime.combine(
             value, datetime.time.min, tzinfo=datetime.timezone.utc
         )
     return value
 
 
 class Case(LedgerBaseModel[CaseDataDict]):
     """Transient grouping of one patient's examinations during a clinical stay."""
 
     case_id: str
     patient: str
     admission_date: AwareDatetime
     leave_date: AwareDatetime | None = None
     patient_examinations: list[PExamination] = Field(
         default_factory=list,
         validation_alias=AliasChoices(
             "patient_examinations", "examinations", "related_examinations"
         ),
     )
+    report_ids: list[str] = Field(
+        default_factory=list,
+        validation_alias=AliasChoices("report_ids", "reports"),
+    )
+
+    @field_validator("report_ids", mode="before")
+    @classmethod
+    def _coerce_report_ids(cls, value: Any) -> list[str]:
+        if value is None:
+            return []
+        if not isinstance(value, list):
+            raise ValueError("report_ids must be a list of references")
+        report_ids: list[str] = []
+        for report_ref in value:
+            if isinstance(report_ref, str):
+                report_ids.append(report_ref)
+            elif isinstance(report_ref, dict):
+                if "uuid" in report_ref:
+                    report_ids.append(str(report_ref["uuid"]))
+                elif "id" in report_ref:
+                    report_ids.append(str(report_ref["id"]))
+                else:
+                    raise ValueError(
+                        "report_ids dict items must contain 'uuid' or 'id'"
+                    )
+            else:
+                if hasattr(report_ref, "uuid"):
+                    report_ids.append(str(getattr(report_ref, "uuid")))
+                else:
+                    raise ValueError(
+                        "report_ids list items must be strings or objects with uuid"
+                    )
+        return report_ids
 
     @field_validator("admission_date", "leave_date", mode="before")
     @classmethod
     def normalize_case_date(cls, value: Any) -> Any:
         return _as_aware_datetime(value)
 
     @model_validator(mode="after")
     def validate_group(self) -> Case:
         if self.leave_date is not None and self.leave_date < self.admission_date:
             raise ValueError("leave_date must not be earlier than admission_date")
 
         mismatched = [
             str(examination.uuid)
             for examination in self.patient_examinations
             if examination.patient != self.patient
         ]
         if mismatched:
             raise ValueError(
                 "patient_examinations must belong to the case patient; "
                 f"mismatched examination UUIDs: {', '.join(mismatched)}"
             )
         return self
 
     @classmethod
     def list_type_fields(cls) -> list[str]:
         return CASE_MODEL_LIST_TYPE_FIELDS
 
     @property
     def ddict_class(self) -> type[CaseDataDict]:
         return CaseDataDict
 
     @classmethod
     def nested_fields(cls) -> list[str]:
-        return CASE_MODEL_NESTED_FIELDS
+        return CASE_MODEL_NESTED_FIELDS + ["report_ids"]
 
     @property
     def serialized_ddict_class(self) -> type[SerializedCaseDataDict]:
         return SerializedCaseDataDict
 
     @classmethod
     def serialized_model_class(cls) -> type[SerializedCase]:
         return SerializedCase
 
 
 class SerializedCase(LedgerBaseModel[SerializedCaseDataDict]):
     case_id: str
     patient: str
     admission_date: AwareDatetime
     leave_date: AwareDatetime | None = None
     patient_examinations: str = ""
+    report_ids: str = ""
 
     @classmethod
     def list_type_fields(cls) -> list[str]:
         return CASE_MODEL_LIST_TYPE_FIELDS
 
     @property
     def ddict_class(self) -> type[SerializedCaseDataDict]:
         return SerializedCaseDataDict
 
     @classmethod
     def nested_fields(cls) -> list[str]:
         return []
diff --git lx_dtypes/models/ledger/main.py lx_dtypes/models/ledger/main.py
index 73e4f85..008204e 100644
--- lx_dtypes/models/ledger/main.py
+++ lx_dtypes/models/ledger/main.py
@@ -1,337 +1,367 @@
 from typing import List, Literal, Union
 
 from .case import (
     LCaseLookupType,
     l_case_ddicts,
     l_case_lookup,
     l_case_models,
 )
 
 from .center import (
     LCenterDjangoLookupType,
     LCenterLookupType,
     l_center_ddicts,
     l_center_django_lookup,
     l_center_django_models,
     l_center_lookup,
     l_center_models,
 )
 from .examiner import (
     LExaminerDjangoLookupType,
     LExaminerLookupType,
     l_examiner_ddicts,
     l_examiner_django_lookup,
     l_examiner_django_models,
     l_examiner_lookup,
     l_examiner_models,
 )
 from .medical import (
     LMedicalLookupType,
     l_medical_ddicts,
     l_medical_lookup,
     l_medical_models,
 )
 from .p_examination import (
     LPExaminationDjangoLookupType,
     LPExaminationLookupType,
     l_p_examination_ddicts,
     l_p_examination_django_lookup,
     l_p_examination_django_models,
     l_p_examination_lookup,
     l_p_examination_models,
 )
 from .p_finding import (
     LPFindingDjangoLookupType,
     LPFindingLookupType,
     l_p_finding_ddicts,
     l_p_finding_django_lookup,
     l_p_finding_django_models,
     l_p_finding_lookup,
     l_p_finding_models,
 )
 from .p_finding_classification_choice import (
     LPFindingClassificationChoiceDjangoLookupType,
     LPFindingClassificationChoiceLookupType,
     l_p_finding_classification_choice_ddicts,
     l_p_finding_classification_choice_django_lookup,
     l_p_finding_classification_choice_django_models,
     l_p_finding_classification_choice_lookup,
     l_p_finding_classification_choice_models,
 )
 from .p_finding_classification_choice_descriptor import (
     LPFindingClassificationChoiceDescriptorDjangoLookupType,
     LPFindingClassificationChoiceDescriptorLookupType,
     l_p_finding_classification_choice_descriptor_ddicts,
     l_p_finding_classification_choice_descriptor_django_lookup,
     l_p_finding_classification_choice_descriptor_django_models,
     l_p_finding_classification_choice_descriptor_lookup,
     l_p_finding_classification_choice_descriptor_models,
 )
 from .p_finding_classifications import (
     LPFindingClassificationsDjangoLookupType,
     LPFindingClassificationsLookupType,
     l_p_finding_classifications_ddicts,
     l_p_finding_classifications_django_lookup,
     l_p_finding_classifications_django_models,
     l_p_finding_classifications_lookup,
     l_p_finding_classifications_models,
 )
 from .p_indication import (
     LPIndicationDjangoLookupType,
     LPIndicationLookupType,
     l_p_indication_ddicts,
     l_p_indication_django_lookup,
     l_p_indication_django_models,
     l_p_indication_lookup,
     l_p_indication_models,
 )
 from .p_indication_classification import (
     LPIndicationClassificationDjangoLookupType,
     LPIndicationClassificationLookupType,
     l_p_indication_classification_ddicts,
     l_p_indication_classification_django_lookup,
     l_p_indication_classification_django_models,
     l_p_indication_classification_lookup,
     l_p_indication_classification_models,
 )
 from .p_indication_classification_descriptor import (
     LPIndicationClassificationDescriptorDjangoLookupType,
     LPIndicationClassificationDescriptorLookupType,
     l_p_indication_classification_descriptor_ddicts,
     l_p_indication_classification_descriptor_django_lookup,
     l_p_indication_classification_descriptor_django_models,
     l_p_indication_classification_descriptor_lookup,
     l_p_indication_classification_descriptor_models,
 )
 from .p_intervention import (
     LPFindingInterventionDjangoLookupType,
     LPFindingInterventionLookupType,
     l_p_finding_intervention_ddicts,
     l_p_finding_intervention_django_lookup,
     l_p_finding_intervention_django_models,
     l_p_finding_intervention_lookup,
     l_p_finding_intervention_models,
 )
 from .p_interventions import (
     LPFindingInterventionsDjangoLookupType,
     LPFindingInterventionsLookupType,
     l_p_finding_interventions_ddicts,
     l_p_finding_interventions_django_lookup,
     l_p_finding_interventions_django_models,
     l_p_finding_interventions_lookup,
     l_p_finding_interventions_models,
 )
 from .p_video import (
     LPVideoLookupType,
     l_p_video_ddicts,
     # LPVideoDjangoLookupType, # TODO
     # l_p_video_django_lookup, # TODO
     # l_p_video_django_models, # TODO
     l_p_video_lookup,
     l_p_video_models,
 )
+from .report import (
+    LReportLookupType,
+    l_report_ddicts,
+    l_report_lookup,
+    l_report_models,
+)
+from .video_file import (
+    LVidFileDjangoLookupType,
+    LVidFileLookupType,
+    l_vid_file_ddicts,
+    l_vid_file_django_lookup,
+    l_vid_file_django_models,
+    l_vid_file_lookup,
+    l_vid_file_models,
+)
 from .patient import (
     LPatientDjangoLookupType,
     LPatientLookupType,
     l_patient_ddicts,
     l_patient_django_lookup,
     l_patient_django_models,
     l_patient_lookup,
     l_patient_models,
 )
 
 
 class LedgerModelsLookupType(
     LCenterLookupType,
     LPExaminationLookupType,
+    LReportLookupType,
     LCaseLookupType,
     LExaminerLookupType,
     LPFindingLookupType,
     LPIndicationLookupType,
     LPIndicationClassificationLookupType,
     LPIndicationClassificationDescriptorLookupType,
     LPFindingClassificationsLookupType,
     LPFindingClassificationChoiceLookupType,
     LPFindingClassificationChoiceDescriptorLookupType,
     LPFindingInterventionsLookupType,
     LPFindingInterventionLookupType,
     LPatientLookupType,
     LPVideoLookupType,
+    LVidFileLookupType,
     LMedicalLookupType,
 ):
     pass
 
 
 ledger_models_lookup = LedgerModelsLookupType(
     **l_center_lookup,
     **l_p_examination_lookup,
     **l_case_lookup,
+    **l_report_lookup,
     **l_examiner_lookup,
     **l_p_finding_lookup,
     **l_p_indication_lookup,
     **l_p_indication_classification_lookup,
     **l_p_indication_classification_descriptor_lookup,
     **l_p_finding_classifications_lookup,
     **l_p_finding_classification_choice_lookup,
     **l_p_finding_classification_choice_descriptor_lookup,
     **l_p_finding_interventions_lookup,
     **l_p_finding_intervention_lookup,
     **l_patient_lookup,
     **l_p_video_lookup,
+    **l_vid_file_lookup,
     **l_medical_lookup,
 )
 
 
 class LedgerModelsDjangoLookupType(
     LCenterDjangoLookupType,
     LPExaminationDjangoLookupType,
     LExaminerDjangoLookupType,
     LPFindingDjangoLookupType,
     LPIndicationDjangoLookupType,
     LPIndicationClassificationDjangoLookupType,
     LPIndicationClassificationDescriptorDjangoLookupType,
     LPFindingClassificationsDjangoLookupType,
     LPFindingClassificationChoiceDjangoLookupType,
     LPFindingClassificationChoiceDescriptorDjangoLookupType,
     LPFindingInterventionsDjangoLookupType,
     LPFindingInterventionDjangoLookupType,
     LPatientDjangoLookupType,
+    LVidFileDjangoLookupType,
     # LPVideoDjangoLookupType, # TODO
 ):
     pass
 
 
 ledger_models_django_lookup: LedgerModelsDjangoLookupType = (
     LedgerModelsDjangoLookupType(
         **l_center_django_lookup,
         **l_p_examination_django_lookup,
         **l_examiner_django_lookup,
         **l_p_finding_django_lookup,
         **l_p_indication_django_lookup,
         **l_p_indication_classification_django_lookup,
         **l_p_indication_classification_descriptor_django_lookup,
         **l_p_finding_classifications_django_lookup,
         **l_p_finding_classification_choice_django_lookup,
         **l_p_finding_classification_choice_descriptor_django_lookup,
         **l_p_finding_interventions_django_lookup,
         **l_p_finding_intervention_django_lookup,
         **l_patient_django_lookup,
+        **l_vid_file_django_lookup,
         # **l_p_video_django_lookup, # TODO
     )
 )
 
 L_MODELS = Union[
     l_center_models,
     l_p_examination_models,
     l_case_models,
+    l_report_models,
     l_examiner_models,
     l_p_finding_models,
     l_p_indication_models,
     l_p_indication_classification_models,
     l_p_indication_classification_descriptor_models,
     l_p_finding_classifications_models,
     l_p_finding_classification_choice_models,
     l_p_finding_classification_choice_descriptor_models,
     l_p_finding_interventions_models,
     l_p_finding_intervention_models,
+    l_vid_file_models,
     l_patient_models,
     l_p_video_models,
     l_medical_models,
 ]
 
 L_MODELS_DJANGO = Union[
     l_center_django_models,
     l_p_examination_django_models,
     l_examiner_django_models,
     l_p_finding_django_models,
     l_p_indication_django_models,
     l_p_indication_classification_django_models,
     l_p_indication_classification_descriptor_django_models,
     l_p_finding_classifications_django_models,
     l_p_finding_classification_choice_django_models,
     l_p_finding_classification_choice_descriptor_django_models,
     l_p_finding_interventions_django_models,
     l_p_finding_intervention_django_models,
     l_patient_django_models,
+    l_vid_file_django_models,
     # l_p_video_django_models, # TODO
 ]
 
 L_DDICTS = Union[
     l_center_ddicts,
     l_p_examination_ddicts,
     l_case_ddicts,
     l_examiner_ddicts,
     l_p_finding_ddicts,
     l_p_indication_ddicts,
     l_p_indication_classification_ddicts,
     l_p_indication_classification_descriptor_ddicts,
     l_p_finding_classifications_ddicts,
     l_p_finding_classification_choice_ddicts,
     l_p_finding_classification_choice_descriptor_ddicts,
     l_p_finding_interventions_ddicts,
     l_p_finding_intervention_ddicts,
     l_patient_ddicts,
+    l_vid_file_ddicts,
+    l_report_ddicts,
     l_p_video_ddicts,
     l_medical_ddicts,
 ]
 L_MODEL_NAMES_LITERAL = Literal[
     "Center",
     "Examiner",
     "Patient",
     "PExamination",
     "Case",
+    "Report",
     "PFinding",
     "PIndication",
     "PIndicationClassification",
     "PIndicationClassificationDescriptor",
     "PFindingClassifications",
     "PFindingClassificationChoice",
     "PFindingInterventions",
     "PFindingIntervention",
+    "VideoFile",
     "PatientVideoFile",
     "RawPatientVideoFile",
     "PatientDisease",
     "PatientEvent",
     "PatientLabSample",
     "PatientLabValue",
     "PatientMedication",
     "PatientMedicationSchedule",
     "PatientMedicalLedger",
 ]
 
 L_MODEL_NAMES_ORDERED: List[L_MODEL_NAMES_LITERAL] = [
     "Center",
     "Examiner",
     "Patient",
     "PExamination",
     "Case",
+    "Report",
     "PFinding",
     "PIndication",
     "PIndicationClassification",
     "PIndicationClassificationDescriptor",
     "PFindingClassifications",
     "PFindingClassificationChoice",
     "PFindingInterventions",
     "PFindingIntervention",
+    "VideoFile",
     "PatientVideoFile",
     "RawPatientVideoFile",
     "PatientDisease",
     "PatientEvent",
     "PatientLabSample",
     "PatientLabValue",
     "PatientMedication",
     "PatientMedicationSchedule",
     "PatientMedicalLedger",
 ]
 
 __all__ = [
     "L_MODELS",
     "L_MODELS_DJANGO",
     "L_DDICTS",
     "ledger_models_lookup",
     "LedgerModelsLookupType",
     "ledger_models_django_lookup",
     "LedgerModelsDjangoLookupType",
     "L_MODEL_NAMES_LITERAL",
     "L_MODEL_NAMES_ORDERED",
 ]
