diff --git a/program/views.py b/program/views.py
index 875368a29b4e9cbc3656ebaa30c3872d376554bb..852481026314cee79a99f4ea4e36e95dfb4c9a37 100644
--- a/program/views.py
+++ b/program/views.py
@@ -39,6 +39,8 @@ from program.models import (
     FundingCategory,
     Host,
     Language,
+    LicenseType,
+    LinkType,
     MusicFocus,
     Note,
     Schedule,
@@ -54,6 +56,8 @@ from program.serializers import (
     FundingCategorySerializer,
     HostSerializer,
     LanguageSerializer,
+    LicenseTypeSerializer,
+    LinkTypeSerializer,
     MusicFocusSerializer,
     NoteSerializer,
     ScheduleConflictResponseSerializer,
@@ -81,14 +85,10 @@ def json_day_schedule(request, year=None, month=None, day=None):
     if year is None and month is None and day is None:
         today = timezone.make_aware(datetime.combine(timezone.now(), time(0, 0)))
     else:
-        today = timezone.make_aware(
-            datetime.combine(date(year, month, day), time(0, 0))
-        )
+        today = timezone.make_aware(datetime.combine(date(year, month, day), time(0, 0)))
 
     timeslots = (
-        TimeSlot.objects.get_24h_timeslots(today)
-        .select_related("schedule")
-        .select_related("show")
+        TimeSlot.objects.get_24h_timeslots(today).select_related("schedule").select_related("show")
     )
     schedule = []
     for ts in timeslots:
@@ -520,9 +520,7 @@ class APIScheduleViewSet(
         schedule = self.get_object()
 
         # If default playlist id or repetition are given, just update
-        if default_playlist_id := request.data.get("schedule").get(
-            "default_playlist_id"
-        ):
+        if default_playlist_id := request.data.get("schedule").get("default_playlist_id"):
             schedule.default_playlist_id = int(default_playlist_id)
             schedule.save()
 
@@ -537,9 +535,7 @@ class APIScheduleViewSet(
             return Response(serializer.data)
 
         try:
-            resolution = Schedule.resolve_conflicts(
-                request.data, schedule.pk, schedule.show.pk
-            )
+            resolution = Schedule.resolve_conflicts(request.data, schedule.pk, schedule.show.pk)
         except ScheduleConflictError as exc:
             return Response(exc.conflicts, status.HTTP_409_CONFLICT)
 
@@ -599,9 +595,8 @@ class APITimeSlotViewSet(
     def update(self, request, *args, **kwargs):
         show_pk = get_values(self.kwargs, "show_pk")
 
-        if (
-            not request.user.is_superuser
-            and show_pk not in request.user.shows.values_lis("id", flat=True)
+        if not request.user.is_superuser and show_pk not in request.user.shows.values_lis(
+            "id", flat=True
         ):
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
@@ -669,9 +664,8 @@ class APINoteViewSet(
         """
         show_pk, timeslot_pk = get_values(self.kwargs, "show_pk", "timeslot_pk")
 
-        if (
-            not request.user.is_superuser
-            and show_pk not in request.user.shows.values_list("id", flat=True)
+        if not request.user.is_superuser and show_pk not in request.user.shows.values_list(
+            "id", flat=True
         ):
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
@@ -681,9 +675,7 @@ class APINoteViewSet(
         )
 
         if serializer.is_valid():
-            hosts = Host.objects.filter(
-                shows__in=request.user.shows.values_list("id", flat=True)
-            )
+            hosts = Host.objects.filter(shows__in=request.user.shows.values_list("id", flat=True))
             if not request.user.is_superuser and request.data["host"] not in hosts:
                 serializer.validated_data["host"] = None
 
@@ -698,9 +690,8 @@ class APINoteViewSet(
         """
         show_pk = get_values(self.kwargs, "show_pk")
 
-        if (
-            not request.user.is_superuser
-            and show_pk not in request.user.shows.values_list("id", flat=True)
+        if not request.user.is_superuser and show_pk not in request.user.shows.values_list(
+            "id", flat=True
         ):
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
@@ -710,14 +701,10 @@ class APINoteViewSet(
         )
 
         if serializer.is_valid():
-            hosts = Host.objects.filter(
-                shows__in=request.user.shows.values_list("id", flat=True)
-            )
+            hosts = Host.objects.filter(shows__in=request.user.shows.values_list("id", flat=True))
             # Don't assign a host the user mustn't edit. Reassign the original value instead
             if not request.user.is_superuser and int(request.data["host"]) not in hosts:
-                serializer.validated_data["host"] = Host.objects.filter(
-                    pk=note.host_id
-                )[0]
+                serializer.validated_data["host"] = Host.objects.filter(pk=note.host_id)[0]
 
             serializer.save()
             return Response(serializer.data)
@@ -730,9 +717,8 @@ class APINoteViewSet(
         """
         show_pk = get_values(self.kwargs, "show_pk")
 
-        if (
-            not request.user.is_superuser
-            and show_pk not in request.user.shows.values_list("id", flat=True)
+        if not request.user.is_superuser and show_pk not in request.user.shows.values_list(
+            "id", flat=True
         ):
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
@@ -801,9 +787,7 @@ class APIMusicFocusViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
     create=extend_schema(summary="Create a new funding category."),
     retrieve=extend_schema(summary="Retrieve a single funding category."),
     update=extend_schema(summary="Update an existing funding category."),
-    partial_update=extend_schema(
-        summary="Partially update an existing funding category."
-    ),
+    partial_update=extend_schema(summary="Partially update an existing funding category."),
     destroy=extend_schema(summary="Delete an existing funding category."),
     list=extend_schema(summary="List all funding categories."),
 )
@@ -866,3 +850,29 @@ class APIHostViewSet(ActiveFilterMixin, viewsets.ModelViewSet):
     def partial_update(self, request, *args, **kwargs):
         kwargs["partial"] = True
         return self.update(request, *args, **kwargs)
+
+
+@extend_schema_view(
+    create=extend_schema(summary="Create a new link type."),
+    retrieve=extend_schema(summary="Retrieve a single link type."),
+    update=extend_schema(summary="Update an existing link type."),
+    partial_update=extend_schema(summary="Partially update an existing link type."),
+    destroy=extend_schema(summary="Delete an existing link type."),
+    list=extend_schema(summary="List all link types."),
+)
+class APILinkTypeViewSet(viewsets.ModelViewSet):
+    queryset = LinkType.objects.all()
+    serializer_class = LinkTypeSerializer
+
+
+@extend_schema_view(
+    create=extend_schema(summary="Create a new license type."),
+    retrieve=extend_schema(summary="Retrieve a single license type."),
+    update=extend_schema(summary="Update an existing license type."),
+    partial_update=extend_schema(summary="Partially update an existing license type."),
+    destroy=extend_schema(summary="Delete an existing license type."),
+    list=extend_schema(summary="List all license types."),
+)
+class APILicenseTypeViewSet(viewsets.ModelViewSet):
+    queryset = LicenseType.objects.all()
+    serializer_class = LicenseTypeSerializer