diff --git a/pyproject.toml b/pyproject.toml
index ad9da6437b783650494d4f2a7a3bcc3d1615144e..15e4cade3952ee576715e8527baa5a7d9fb66d97 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -35,7 +35,8 @@ disable_error_code = ["annotation-unchecked"]
 exclude = [
     "migrations",
     "tests",
-    "dev"
+    "dev",
+    "scripts",
 ]
 
 [tool.black]
diff --git a/running_exercise/lib/email_client.py b/running_exercise/lib/email_client.py
index 7bf5392a9abd7d23a85b0d55c924817a3cf39499..e58a3fd5096af67bb88417eeae8aee49acd79893 100644
--- a/running_exercise/lib/email_client.py
+++ b/running_exercise/lib/email_client.py
@@ -67,47 +67,39 @@ def send_email(
 
 def _validate_participants(
     participant_addresses: List[str], exercise: Exercise
-) -> List[EmailParticipant]:
-    if len(set(participant_addresses)) < 2:
+) -> QuerySet[EmailParticipant]:
+    distinct_addresses = set(participant_addresses)
+    if len(distinct_addresses) < 2:
         raise RunningExerciseOperationException(
             "A thread must have at least 2 participants"
         )
 
-    email_between_teams = exercise.config.has_enabled(
-        Feature.EMAIL_BETWEEN_TEAMS
+    participants = EmailParticipant.objects.filter(
+        exercise_id=exercise.id, address__in=distinct_addresses
     )
-    participants: List[EmailParticipant] = []
-    previous_definition_participant: Optional[EmailParticipant] = None
-    has_team = False
-
-    for address in participant_addresses:
-        # TODO: Handle email address that is not included in the definition,
-        # needs to be logged somewhere, however action logs do not seem as
-        # the best choice, because I am not sure if this should be included
-        # in the trainee visible logs
-        # maybe add a new action log type for errors, which can be used for filtering
-        participant = get_model(
-            EmailParticipant, exercise=exercise, address=address
+
+    if len(participants) != len(distinct_addresses):
+        # how much do we care about the error message?
+        raise RunningExerciseOperationException(
+            f"Some address/es from this list `{distinct_addresses}` don't exist in the exercise"
         )
-        if participant.is_definition():
-            if previous_definition_participant is not None:
-                raise RunningExerciseOperationException(
-                    f"Cannot send email to these addresses at the same time: "
-                    f"'{previous_definition_participant.address}',"
-                    f" '{participant.address}'"
-                )
-            previous_definition_participant = participant
-        else:
-            # this is probably an extremely rare edge case, however there is a possibility
-            # that a team randomly guesses another teams address and without this check
-            # the 'email_between_teams' feature could be bypassed
-            if has_team and not email_between_teams:
-                raise RunningExerciseOperationException(
-                    "Email communication between teams is disabled"
-                )
-            has_team = True
-
-        participants.append(participant)
+
+    team_participants_count = sum(
+        map(lambda participant: participant.team_id is not None, participants)
+    )
+
+    if team_participants_count == 0:
+        raise RunningExerciseOperationException(
+            f"A thread must contain at least 1 team address"
+        )
+
+    if team_participants_count > 1 and not exercise.config.has_enabled(
+        Feature.EMAIL_BETWEEN_TEAMS
+    ):
+        raise RunningExerciseOperationException(
+            f"Email between teams is not enabled"
+        )
+
     return participants
 
 
@@ -118,17 +110,21 @@ def _activate_thread_definition_milestones(
     This method handles proper milestone activation for milestones specified in
     EmailAddress
     """
-    team_email_sent = (
-        thread.emails.filter(sender__definition_address__isnull=True).count()
-        > 0
-    )
+    # Instructor emails should not activate milestones
+    if sender.is_definition():
+        return
 
-    if team_email_sent or sender.is_definition():
+    # If a team already sent an email to this thread, do not activate milestones again
+    team_email_sent = thread.emails.filter(
+        sender__definition_address__isnull=True
+    ).exists()
+
+    if team_email_sent:
         return
 
-    if (
-        definition_participant := thread.get_definition_participant()
-    ) is not None:
+    for definition_participant in thread.participants.filter(
+        definition_address__isnull=False
+    ).select_related("definition_address__control"):
         _update_thread_milestones(
             thread, definition_participant.definition_address.control
         )
@@ -147,9 +143,7 @@ class EmailClient:
         )
         new_thread.participants.add(*participants)
 
-        SubscriptionHandler.broadcast_email_thread(
-            new_thread, participants[0].exercise
-        )
+        SubscriptionHandler.broadcast_email_thread(new_thread, exercise)
         return new_thread
 
     @staticmethod
diff --git a/running_exercise/schema/query.py b/running_exercise/schema/query.py
index 5b7ea3b5eb803ec46362124c4b34edfcc04f16ac..afaf33d3b0e2f1f1cc67eb07b01d5e6280e595aa 100644
--- a/running_exercise/schema/query.py
+++ b/running_exercise/schema/query.py
@@ -353,17 +353,20 @@ class Query(graphene.ObjectType):
     @protected(User.AuthGroup.INSTRUCTOR)
     def resolve_thread_templates(
         self, info, thread_id: int
-    ) -> List[EmailTemplate]:
+    ) -> QuerySet[EmailTemplate]:
         thread = get_model(EmailThread, id=thread_id)
         exercise_access(info.context, thread.exercise_id)
 
-        definition_participant = thread.get_definition_participant()
-        if definition_participant is None:
-            return []
+        definition_participants = thread.participants.filter(
+            definition_address__isnull=False
+        ).select_related("definition_address")
 
-        return list(
-            definition_participant.get_definition_address().templates.all()
-        )
+        templates = EmailTemplate.objects.none()
+
+        for participant in definition_participants:
+            templates |= participant.definition_address.templates.all()
+
+        return templates
 
     @protected(User.AuthGroup.INSTRUCTOR)
     def resolve_thread_template(self, info, template_id: str) -> EmailTemplate:
diff --git a/running_exercise/tests/email_tests.py b/running_exercise/tests/email_tests.py
index 5b20b1de131632d2fc33d22aa16eff1951491c17..152787219a29ae8243b36a4ba2cf950d3c52b7cd 100644
--- a/running_exercise/tests/email_tests.py
+++ b/running_exercise/tests/email_tests.py
@@ -128,22 +128,15 @@ class EmailTests(TestCase):
                 participants, self.subject, str(self.exercise.id)
             )
 
+        participants.append("team-1@mail.com")
         participants.append("nonexistentaddress@mail.com")
-        with self.assertRaises(ModelNotFoundException) as _:
-            EmailClient.create_thread(
-                participants, self.subject, str(self.exercise.id)
-            )
-
-        # multiple definition addresses
-        participants[1] = "test@mail.ex"
         with self.assertRaises(RunningExerciseOperationException) as _:
             EmailClient.create_thread(
                 participants, self.subject, str(self.exercise.id)
             )
 
         _disable_feature(self.exercise, Feature.EMAIL_BETWEEN_TEAMS)
-        participants[1] = "team-1@mail.com"
-        participants.append("team-2@mail.com")
+        participants[0] = "team-2@mail.com"
         with self.assertRaises(RunningExerciseOperationException) as _:
             EmailClient.create_thread(
                 participants, self.subject, str(self.exercise.id)
@@ -190,6 +183,17 @@ class EmailTests(TestCase):
             self.subject,
         )
 
+        # thread with multiple definition addresses
+        participants = ["team-1@mail.com", "test@mail.ex", "doe@mail.ex"]
+        thread = EmailClient.create_thread(
+            participants, self.subject, str(self.exercise.id)
+        )
+
+        self._compare_expected_participants(
+            participants, thread.participants.all()
+        )
+        self.assertEqual(thread.subject, self.subject)
+
     def test_get_thread(self):
         # each combination consist of a definition and a team address,
         # the team address is first
@@ -355,8 +359,11 @@ class EmailTests(TestCase):
             )
 
     def test_team_email(self):
-        participants = ["doe@mail.ex", "team-1@mail.com"]
-        milestone = self.definition.milestones.get(name="email_doe")
+        participants = ["doe@mail.ex", "test@mail.ex", "team-1@mail.com"]
+        email_doe = self.definition.milestones.get(name="email_doe")
+        email_sent_to_test = self.definition.milestones.get(
+            name="email_sent_to_test"
+        )
         team_sender = self.participants["team-1@mail.com"]
         definition_sender = self.participants["doe@mail.ex"]
         email_channel = self.definition.channels.get(type=InjectTypes.EMAIL)
@@ -365,7 +372,10 @@ class EmailTests(TestCase):
         )
 
         # check that milestones from the definition address were not activated on thread create
-        self.assertFalse(is_milestone_reached(team_sender.team, milestone))
+        self.assertFalse(is_milestone_reached(team_sender.team, email_doe))
+        self.assertFalse(
+            is_milestone_reached(team_sender.team, email_sent_to_test)
+        )
         file_info = self.definition.files.first()
         action_logs = EmailClient.send_email(
             create_input(
@@ -392,7 +402,10 @@ class EmailTests(TestCase):
         )
         # check that after first email was sent the thread milestones were not activated
         # because it was not sent by a team
-        self.assertFalse(is_milestone_reached(team_sender.team, milestone))
+        self.assertFalse(is_milestone_reached(team_sender.team, email_doe))
+        self.assertFalse(
+            is_milestone_reached(team_sender.team, email_sent_to_test)
+        )
 
         action_logs = EmailClient.send_email(
             create_input(
@@ -411,9 +424,12 @@ class EmailTests(TestCase):
             f"'{sent_email.sender=}' and '{team_sender}'",
         )
 
-        self.assertTrue(is_milestone_reached(team_sender.team, milestone))
+        self.assertTrue(is_milestone_reached(team_sender.team, email_doe))
+        self.assertTrue(
+            is_milestone_reached(team_sender.team, email_sent_to_test)
+        )
 
-        deactivate_milestones(team_sender.team, [milestone])
+        deactivate_milestones(team_sender.team, [email_doe, email_sent_to_test])
 
         action_logs = EmailClient.send_email(
             create_input(
@@ -426,4 +442,7 @@ class EmailTests(TestCase):
         self._check_action_logs_channel(action_logs, email_channel)
 
         # check that further emails do not activate the thread milestones again
-        self.assertFalse(is_milestone_reached(team_sender.team, milestone))
+        self.assertFalse(is_milestone_reached(team_sender.team, email_doe))
+        self.assertFalse(
+            is_milestone_reached(team_sender.team, email_sent_to_test)
+        )