diff --git a/src/api/serializers.py b/src/api/serializers.py
index 2434911e88e10b6c9caf6e33a4e7cb9292c95941..e0341bd899ad99a43ac6e8fe8549e497d41c0611 100644
--- a/src/api/serializers.py
+++ b/src/api/serializers.py
@@ -11,9 +11,10 @@ from core.models.assemblies import Assembly
 from core.models.badges import Badge, BadgeToken, BadgeTokenTimeConstraint
 from core.models.conference import Conference, ConferenceMember, ConferenceTrack
 from core.models.events import Event
+from core.models.messages import DirectMessage
 from core.models.metanavi import MetaNavItem
 from core.models.rooms import Room
-from core.models.users import UserTimelineEntry
+from core.models.users import PlatformUser, UserTimelineEntry
 
 
 class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField):
@@ -297,3 +298,50 @@ class MetaNavItemSerializer(HubModelSerializer):
             'graphic_light',
             'graphic_dark',
         ]
+
+
+class PlatformUserVisibleSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = PlatformUser
+        fields = ['username', 'display_name']
+        read_only_fields = fields
+
+
+class DirectMessageSerializerSentShort(HubModelSerializer):
+    class Meta:
+        model = DirectMessage
+        fields = ['id', 'sender', 'recipient', 'timestamp', 'in_reply_to', 'subject']
+        read_only_fields = fields
+
+    sender = PlatformUserVisibleSerializer(read_only=True)
+    recipient = PlatformUserVisibleSerializer(read_only=True)
+
+
+class DirectMessageSerializerReceivedShort(HubModelSerializer):
+    class Meta:
+        model = DirectMessage
+        fields = [*DirectMessageSerializerSentShort.Meta.fields, 'has_responded']
+        read_only_fields = fields
+
+    sender = PlatformUserVisibleSerializer(read_only=True)
+    recipient = PlatformUserVisibleSerializer(read_only=True)
+
+
+class DirectMessageSerializerSent(HubModelSerializer):
+    class Meta:
+        model = DirectMessage
+        fields = [*DirectMessageSerializerSentShort.Meta.fields, 'body']
+        read_only_fields = fields
+
+    sender = PlatformUserVisibleSerializer(read_only=True)
+    recipient = PlatformUserVisibleSerializer(read_only=True)
+
+
+class DirectMessageSerializerReceived(HubModelSerializer):
+    class Meta:
+        model = DirectMessage
+        fields = [*DirectMessageSerializerReceivedShort.Meta.fields, 'body']
+        read_only_fields = [f for f in fields if f != 'was_read']
+
+    sender = PlatformUserVisibleSerializer(read_only=True)
+    recipient = PlatformUserVisibleSerializer(read_only=True)
diff --git a/src/api/tests/__init__.py b/src/api/tests/__init__.py
index 8fb7973a17a79486bf8775d3b886df46eb305234..5f3d708f7a0cb7923a0e711001468b5e408cf603 100644
--- a/src/api/tests/__init__.py
+++ b/src/api/tests/__init__.py
@@ -1,7 +1,9 @@
 from .badges import *  # noqa: F401, F403
 from .bbb import *  # noqa: F401, F403
 from .engelsystem import *  # noqa: F401, F403
+from .events import *  # noqa: F401, F403
 from .map import *  # noqa: F401, F403
+from .messages import *  # noqa: F401, F403
 from .metrics import *  # noqa: F401, F403
 from .schedule import *  # noqa: F401, F403
 from .workadventure import *  # noqa: F401, F403
diff --git a/src/api/tests/events.py b/src/api/tests/events.py
new file mode 100644
index 0000000000000000000000000000000000000000..581e8f958a64f6ad37531bd4dfaf47ee045746b4
--- /dev/null
+++ b/src/api/tests/events.py
@@ -0,0 +1,73 @@
+import uuid
+from datetime import datetime, timedelta
+
+from pytz import UTC
+
+from django.test import Client, TestCase, override_settings
+from django.urls import reverse
+
+from core.models import Assembly, Conference, ConferenceMember, Event, PlatformUser
+
+TEST_CONF_ID = uuid.uuid4()
+
+
+@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
+class EventsTestCase(TestCase):
+    def setUp(self):
+        self.conference1 = Conference(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True)
+        self.conference1.save()
+
+        self.human_user = PlatformUser(username='bernd', user_type=PlatformUser.Type.HUMAN)
+        self.human_user.save()
+        self.human_cm = ConferenceMember(conference=self.conference1, user=self.human_user)
+        self.human_cm.save()
+
+        assembly = Assembly(conference=self.conference1, name='DUMMY', slug='dummy', state_assembly=Assembly.State.ACCEPTED, is_official=True)
+        assembly.save()
+
+        self.event = Event(
+            conference=self.conference1,
+            slug='event1',
+            assembly=assembly,
+            name='Event1_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
+            kind=Event.Kind.OFFICIAL,
+        )
+        self.event.save()
+
+    def test_EventMyFavorites(self):
+        c = Client()
+        resp = c.get(reverse('api:my-events'))
+        self.assertEqual(resp.status_code, 403)
+        c.force_login(self.human_user)
+
+        resp = c.get(reverse('api:my-events'))
+        self.assertEqual(resp.json(), [])
+
+        self.human_user.favorite_events.add(self.event)
+        resp = c.get(reverse('api:my-events'))
+        self.assertEqual(resp.json(), [str(self.event.pk)])
+
+    def test_EventMyFavoritesSet_put(self):
+        c = Client()
+        resp = c.put(reverse('api:my-events-set', kwargs={'pk': str(uuid.uuid4())}))
+        self.assertEqual(resp.status_code, 403)
+        c.force_login(self.human_user)
+
+        self.assertEqual(list(self.human_user.favorite_events.all()), [])
+        resp = c.put(reverse('api:my-events-set', kwargs={'pk': str(self.event.pk)}))
+        self.assertEqual(list(self.human_user.favorite_events.all()), [self.event])
+
+    def test_EventMyFavoritesSet_delete(self):
+        c = Client()
+        resp = c.delete(reverse('api:my-events-set', kwargs={'pk': str(uuid.uuid4())}))
+        self.assertEqual(resp.status_code, 403)
+        c.force_login(self.human_user)
+
+        self.human_user.favorite_events.add(self.event)
+
+        self.assertEqual(list(self.human_user.favorite_events.all()), [self.event])
+        resp = c.delete(reverse('api:my-events-set', kwargs={'pk': str(self.event.pk)}))
+        self.assertEqual(list(self.human_user.favorite_events.all()), [])
diff --git a/src/api/tests/messages.py b/src/api/tests/messages.py
new file mode 100644
index 0000000000000000000000000000000000000000..b80b0e9f64a3275ee6a71471de3f4f2ed875460e
--- /dev/null
+++ b/src/api/tests/messages.py
@@ -0,0 +1,261 @@
+import uuid
+from datetime import datetime
+
+from pytz import UTC
+
+from django.test import Client, TestCase, override_settings
+from django.urls import reverse
+from django.utils.timezone import localtime
+
+from core.models import Conference, ConferenceMember, DirectMessage, PlatformUser
+
+TEST_CONF_ID = uuid.uuid4()
+
+
+@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID)
+class MessagesTestCase(TestCase):
+    def setUp(self):
+        self.conference1 = Conference(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True)
+        self.conference1.save()
+
+        self.human_user1 = PlatformUser(username='bernd', user_type=PlatformUser.Type.HUMAN)
+        self.human_user1.save()
+        self.human_cm1 = ConferenceMember(conference=self.conference1, user=self.human_user1)
+        self.human_cm1.save()
+
+        self.human_user2 = PlatformUser(username='björnd', user_type=PlatformUser.Type.HUMAN)
+        self.human_user2.save()
+        self.human_cm2 = ConferenceMember(conference=self.conference1, user=self.human_user2)
+        self.human_cm2.save()
+
+    def test_DirectMessagesReceived(self):
+        c = Client()
+        resp = c.get(reverse('api:my-messages-received'))
+        self.assertEqual(resp.status_code, 403)
+        c.force_login(self.human_user1)
+
+        resp = c.get(reverse('api:my-messages-received'))
+        self.assertEqual(resp.json(), [])
+
+        dm = DirectMessage(
+            conference=self.conference1,
+            sender=self.human_user2,
+            recipient=self.human_user1,
+            timestamp=datetime(2020, 5, 4, 3, 2, 1, tzinfo=UTC),
+            subject='Test Message2',
+            body='Message Body2',
+        )
+        dm.save()
+        resp = c.get(reverse('api:my-messages-received'))
+        self.assertEqual(
+            resp.json(),
+            [
+                {
+                    'id': str(dm.pk),
+                    'sender': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name},
+                    'recipient': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name},
+                    'timestamp': localtime(dm.timestamp).isoformat(),
+                    'in_reply_to': None,
+                    'subject': dm.subject,
+                    'has_responded': False,
+                }
+            ],
+        )
+
+        dm.has_responded = True
+        dm.deleted_by_sender = True
+        dm.save()
+        resp = c.get(reverse('api:my-messages-received'))
+        self.assertEqual(
+            resp.json(),
+            [
+                {
+                    'id': str(dm.pk),
+                    'sender': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name},
+                    'recipient': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name},
+                    'timestamp': localtime(dm.timestamp).isoformat(),
+                    'in_reply_to': None,
+                    'subject': dm.subject,
+                    'has_responded': True,
+                }
+            ],
+        )
+
+        dm.deleted_by_recipient = True
+        dm.save()
+        resp = c.get(reverse('api:my-messages-received'))
+        self.assertEqual(resp.json(), [])
+
+    def test_DirectMessagesSent(self):
+        c = Client()
+        resp = c.get(reverse('api:my-messages-sent'))
+        self.assertEqual(resp.status_code, 403)
+        c.force_login(self.human_user1)
+
+        resp = c.get(reverse('api:my-messages-sent'))
+        self.assertEqual(resp.json(), [])
+
+        dm = DirectMessage(
+            conference=self.conference1,
+            sender=self.human_user1,
+            recipient=self.human_user2,
+            timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC),
+            subject='Test Message',
+            body='Message Body',
+        )
+        dm.save()
+        resp = c.get(reverse('api:my-messages-sent'))
+        self.assertEqual(
+            resp.json(),
+            [
+                {
+                    'id': str(dm.pk),
+                    'sender': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name},
+                    'recipient': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name},
+                    'timestamp': localtime(dm.timestamp).isoformat(),
+                    'in_reply_to': None,
+                    'subject': dm.subject,
+                }
+            ],
+        )
+
+        dm.deleted_by_recipient = True
+        dm.save()
+        resp = c.get(reverse('api:my-messages-sent'))
+        self.assertEqual(len(resp.json()), 1)
+
+        dm.deleted_by_sender = True
+        dm.save()
+        resp = c.get(reverse('api:my-messages-sent'))
+        self.assertEqual(resp.json(), [])
+
+    def test_DirectMessageReceived(self):
+        dm = DirectMessage(
+            conference=self.conference1,
+            sender=self.human_user2,
+            recipient=self.human_user1,
+            timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC),
+            subject='Test Message',
+            body='Message Body',
+        )
+        dm.save()
+
+        other_dm = DirectMessage(
+            conference=self.conference1,
+            sender=self.human_user2,
+            recipient=self.human_user2,
+            timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC),
+            subject='Test Message',
+            body='Message Body',
+        )
+        other_dm.save()
+
+        c = Client()
+        resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(dm.pk)}))
+        self.assertEqual(resp.status_code, 403)
+        c.force_login(self.human_user1)
+
+        resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(other_dm.pk)}))
+        self.assertEqual(resp.status_code, 404)
+
+        resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(dm.pk)}))
+        self.assertEqual(
+            resp.json(),
+            {
+                'id': str(dm.pk),
+                'sender': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name},
+                'recipient': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name},
+                'timestamp': localtime(dm.timestamp).isoformat(),
+                'in_reply_to': None,
+                'subject': dm.subject,
+                'body': dm.body,
+                'has_responded': False,
+            },
+        )
+
+        dm.has_responded = True
+        dm.deleted_by_sender = True
+        dm.save()
+        resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(dm.pk)}))
+        self.assertEqual(
+            resp.json(),
+            {
+                'id': str(dm.pk),
+                'sender': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name},
+                'recipient': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name},
+                'timestamp': localtime(dm.timestamp).isoformat(),
+                'in_reply_to': None,
+                'subject': dm.subject,
+                'body': dm.body,
+                'has_responded': True,
+            },
+        )
+
+        dm.deleted_by_recipient = True
+        dm.save()
+        resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(other_dm.pk)}))
+        self.assertEqual(resp.status_code, 404)
+
+    def test_DirectMessageSent(self):
+        dm = DirectMessage(
+            conference=self.conference1,
+            sender=self.human_user1,
+            recipient=self.human_user2,
+            timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC),
+            subject='Test Message',
+            body='Message Body',
+        )
+        dm.save()
+
+        other_dm = DirectMessage(
+            conference=self.conference1,
+            sender=self.human_user2,
+            recipient=self.human_user2,
+            timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC),
+            subject='Test Message',
+            body='Message Body',
+        )
+        other_dm.save()
+
+        c = Client()
+        resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(dm.pk)}))
+        self.assertEqual(resp.status_code, 403)
+        c.force_login(self.human_user1)
+
+        resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(other_dm.pk)}))
+        self.assertEqual(resp.status_code, 404)
+
+        resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(dm.pk)}))
+        self.assertEqual(
+            resp.json(),
+            {
+                'id': str(dm.pk),
+                'sender': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name},
+                'recipient': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name},
+                'timestamp': localtime(dm.timestamp).isoformat(),
+                'in_reply_to': None,
+                'subject': dm.subject,
+                'body': dm.body,
+            },
+        )
+
+        dm.deleted_by_recipient = True
+        dm.save()
+        resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(dm.pk)}))
+        self.assertEqual(
+            resp.json(),
+            {
+                'id': str(dm.pk),
+                'sender': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name},
+                'recipient': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name},
+                'timestamp': localtime(dm.timestamp).isoformat(),
+                'in_reply_to': None,
+                'subject': dm.subject,
+                'body': dm.body,
+            },
+        )
+
+        dm.deleted_by_sender = True
+        dm.save()
+        resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(other_dm.pk)}))
+        self.assertEqual(resp.status_code, 404)
diff --git a/src/api/urls.py b/src/api/urls.py
index d10c632e9ea06adbb8e5905bf619a5fbd03f60ee..cc96f91ff392a4374f4442305095f678406579ec 100644
--- a/src/api/urls.py
+++ b/src/api/urls.py
@@ -4,7 +4,7 @@ from rest_framework.urlpatterns import format_suffix_patterns
 from django.conf import settings
 from django.urls import path
 
-from .views import api_root, assemblies, badges, bbb, conferencemember, conferences, events, maps, metanav, rooms, schedule, users, workadventure
+from .views import api_root, assemblies, badges, bbb, conferencemember, conferences, events, maps, messages, metanav, rooms, schedule, users, workadventure
 
 app_name = 'api'
 urlpatterns = [
@@ -13,6 +13,12 @@ urlpatterns = [
     path('me', users.profile, name='profile'),
     path('me/badges.zip', users.BadgeExportView.as_view(), name='badge-export'),
     path('me/badges', users.badges, name='badges'),
+    path('me/events', events.EventMyFavorites.as_view(), name='my-events'),
+    path('me/events/<uuid:pk>', events.EventMyFavoritesSet.as_view(), name='my-events-set'),
+    path('me/received-messages/', messages.DirectMessagesReceived.as_view(), name='my-messages-received'),
+    path('me/sent-messages/', messages.DirectMessagesSent.as_view(), name='my-messages-sent'),
+    path('me/received-messages/<uuid:pk>', messages.DirectMessageReceived.as_view(), name='my-message-received'),
+    path('me/sent-messages/<uuid:pk>', messages.DirectMessageSent.as_view(), name='my-message-sent'),
     path('me/friends', users.friends, name='friends'),
     path('me/timeline', users.UserTimelineList.as_view(), name='timeline-list'),
     # conference-specific views
diff --git a/src/api/views/events.py b/src/api/views/events.py
index 44aec250388c25dbbe17f0e5cb5d2264e22730df..dccc9dd8ee1b305a599b6fb831013b0bc75714b9 100644
--- a/src/api/views/events.py
+++ b/src/api/views/events.py
@@ -1,4 +1,6 @@
-from rest_framework import generics
+from rest_framework import generics, permissions
+from rest_framework.response import Response
+from rest_framework.views import APIView
 
 from django.shortcuts import get_object_or_404
 
@@ -21,3 +23,22 @@ class EventDetail(ConferenceSlugMixin, generics.RetrieveAPIView):
     def get_object(self, **kwargs):
         event_id = self.request.resolver_match.kwargs['pk']
         return get_object_or_404(Event.objects.conference_accessible(conference=self.conference), pk=event_id)
+
+
+class EventMyFavorites(ConferenceSlugMixin, generics.ListAPIView):
+    permission_classes = [permissions.IsAuthenticated]
+
+    def list(self, request, *args, **kwargs):
+        return Response(self.request.user.favorite_events.values_list('pk', flat=True))
+
+
+class EventMyFavoritesSet(ConferenceSlugMixin, APIView):
+    permission_classes = [permissions.IsAuthenticated]
+
+    def put(self, request, *args, pk=None, **kwargs):
+        self.request.user.favorite_events.add(*Event.objects.conference_accessible(conference=self.conference).filter(pk=pk))
+        return Response(True)
+
+    def delete(self, request, *args, pk=None, **kwargs):
+        self.request.user.favorite_events.remove(*Event.objects.conference_accessible(conference=self.conference).filter(pk=pk))
+        return Response(True)
diff --git a/src/api/views/messages.py b/src/api/views/messages.py
new file mode 100644
index 0000000000000000000000000000000000000000..3cf37d2bc6bd03435610d793da8b062542817b0f
--- /dev/null
+++ b/src/api/views/messages.py
@@ -0,0 +1,54 @@
+from rest_framework import generics, permissions
+
+from core.models.messages import DirectMessage
+
+from ..serializers import DirectMessageSerializerReceived, DirectMessageSerializerReceivedShort, DirectMessageSerializerSent, DirectMessageSerializerSentShort
+from .mixins import ConferenceSlugMixin
+
+
+class DirectMessagesReceived(ConferenceSlugMixin, generics.ListAPIView):
+    permission_classes = [permissions.IsAuthenticated]
+    serializer_class = DirectMessageSerializerReceivedShort
+
+    def get_queryset(self):
+        return (
+            DirectMessage.objects.filter(conference=self.conference, recipient=self.request.user, deleted_by_recipient=False)
+            .select_related('recipient', 'sender')
+            .order_by('-timestamp')
+        )
+
+
+class DirectMessagesSent(ConferenceSlugMixin, generics.ListAPIView):
+    permission_classes = [permissions.IsAuthenticated]
+    serializer_class = DirectMessageSerializerSentShort
+
+    def get_queryset(self):
+        return (
+            DirectMessage.objects.filter(conference=self.conference, sender=self.request.user, deleted_by_sender=False)
+            .select_related('recipient', 'sender')
+            .order_by('-timestamp')
+        )
+
+
+class DirectMessageReceived(ConferenceSlugMixin, generics.RetrieveUpdateAPIView):
+    permission_classes = [permissions.IsAuthenticated]
+    serializer_class = DirectMessageSerializerReceived
+
+    def get_queryset(self):
+        return (
+            DirectMessage.objects.filter(conference=self.conference, recipient=self.request.user, deleted_by_recipient=False)
+            .select_related('recipient', 'sender')
+            .order_by('-timestamp')
+        )
+
+
+class DirectMessageSent(ConferenceSlugMixin, generics.RetrieveAPIView):
+    permission_classes = [permissions.IsAuthenticated]
+    serializer_class = DirectMessageSerializerSent
+
+    def get_queryset(self):
+        return (
+            DirectMessage.objects.filter(conference=self.conference, sender=self.request.user, deleted_by_sender=False)
+            .select_related('recipient', 'sender')
+            .order_by('-timestamp')
+        )