diff --git a/authentik/events/forms.py b/authentik/events/forms.py
index 7243e01c9..6b2e7acba 100644
--- a/authentik/events/forms.py
+++ b/authentik/events/forms.py
@@ -15,6 +15,7 @@ class NotificationTransportForm(forms.ModelForm):
             "name",
             "mode",
             "webhook_url",
+            "send_once",
         ]
         widgets = {
             "name": forms.TextInput(),
diff --git a/authentik/events/migrations/0012_auto_20210202_1821.py b/authentik/events/migrations/0012_auto_20210202_1821.py
new file mode 100644
index 000000000..36aa9d14e
--- /dev/null
+++ b/authentik/events/migrations/0012_auto_20210202_1821.py
@@ -0,0 +1,52 @@
+# Generated by Django 3.1.6 on 2021-02-02 18:21
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("authentik_events", "0011_notification_rules_default_v1"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="notificationtransport",
+            name="send_once",
+            field=models.BooleanField(
+                default=False,
+                help_text="Only send notification once, for example when sending a webhook into a chat channel.",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="event",
+            name="action",
+            field=models.TextField(
+                choices=[
+                    ("login", "Login"),
+                    ("login_failed", "Login Failed"),
+                    ("logout", "Logout"),
+                    ("user_write", "User Write"),
+                    ("suspicious_request", "Suspicious Request"),
+                    ("password_set", "Password Set"),
+                    ("token_view", "Token View"),
+                    ("invitation_used", "Invite Used"),
+                    ("authorize_application", "Authorize Application"),
+                    ("source_linked", "Source Linked"),
+                    ("impersonation_started", "Impersonation Started"),
+                    ("impersonation_ended", "Impersonation Ended"),
+                    ("policy_execution", "Policy Execution"),
+                    ("policy_exception", "Policy Exception"),
+                    ("property_mapping_exception", "Property Mapping Exception"),
+                    ("system_task_execution", "System Task Execution"),
+                    ("system_task_exception", "System Task Exception"),
+                    ("configuration_error", "Configuration Error"),
+                    ("model_created", "Model Created"),
+                    ("model_updated", "Model Updated"),
+                    ("model_deleted", "Model Deleted"),
+                    ("update_available", "Update Available"),
+                    ("custom_", "Custom Prefix"),
+                ]
+            ),
+        ),
+    ]
diff --git a/authentik/events/models.py b/authentik/events/models.py
index 4e7d933c9..81e3a8b25 100644
--- a/authentik/events/models.py
+++ b/authentik/events/models.py
@@ -184,6 +184,12 @@ class NotificationTransport(models.Model):
     mode = models.TextField(choices=TransportMode.choices)
 
     webhook_url = models.TextField(blank=True)
+    send_once = models.BooleanField(
+        default=False,
+        help_text=_(
+            "Only send notification once, for example when sending a webhook into a chat channel."
+        ),
+    )
 
     def send(self, notification: "Notification") -> list[str]:
         """Send notification to user, called from async task"""
diff --git a/authentik/events/tasks.py b/authentik/events/tasks.py
index 1f30f73a3..43505b8e5 100644
--- a/authentik/events/tasks.py
+++ b/authentik/events/tasks.py
@@ -65,15 +65,17 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
 
     LOGGER.debug("e(trigger): event trigger matched", trigger=trigger)
     # Create the notification objects
-    for user in trigger.group.users.all():
-        notification = Notification.objects.create(
-            severity=trigger.severity, body=event.summary, event=event, user=user
-        )
-
-        for transport in trigger.transports.all():
+    for transport in trigger.transports.all():
+        for user in trigger.group.users.all():
+            LOGGER.debug("created notif")
+            notification = Notification.objects.create(
+                severity=trigger.severity, body=event.summary, event=event, user=user
+            )
             notification_transport.apply_async(
                 args=[notification.pk, transport.pk], queue="authentik_events"
             )
+            if transport.send_once:
+                break
 
 
 @CELERY_APP.task(
diff --git a/authentik/events/tests/test_notifications.py b/authentik/events/tests/test_notifications.py
index 98c23b5fb..ae80c0ea1 100644
--- a/authentik/events/tests/test_notifications.py
+++ b/authentik/events/tests/test_notifications.py
@@ -8,6 +8,7 @@ from authentik.core.models import Group, User
 from authentik.events.models import (
     Event,
     EventAction,
+    Notification,
     NotificationRule,
     NotificationTransport,
 )
@@ -21,7 +22,7 @@ class TestEventsNotifications(TestCase):
 
     def setUp(self) -> None:
         self.group = Group.objects.create(name="test-group")
-        self.user = User.objects.create(name="test-user")
+        self.user = User.objects.create(name="test-user", username="test")
         self.group.users.add(self.user)
         self.group.save()
 
@@ -88,3 +89,26 @@ class TestEventsNotifications(TestCase):
             ):
                 Event.new(EventAction.CUSTOM_PREFIX).save()
         self.assertEqual(passes.call_count, 1)
+
+    def test_transport_once(self):
+        """Test transport's send_once"""
+        user2 = User.objects.create(name="test2-user", username="test2")
+        self.group.users.add(user2)
+        self.group.save()
+
+        transport = NotificationTransport.objects.create(
+            name="transport", send_once=True
+        )
+        NotificationRule.objects.filter(name__startswith="default").delete()
+        trigger = NotificationRule.objects.create(name="trigger", group=self.group)
+        trigger.transports.add(transport)
+        trigger.save()
+        matcher = EventMatcherPolicy.objects.create(
+            name="matcher", action=EventAction.CUSTOM_PREFIX
+        )
+        PolicyBinding.objects.create(target=trigger, policy=matcher, order=0)
+
+        execute_mock = MagicMock()
+        with patch("authentik.events.models.NotificationTransport.send", execute_mock):
+            Event.new(EventAction.CUSTOM_PREFIX).save()
+        self.assertEqual(Notification.objects.count(), 1)
diff --git a/authentik/policies/event_matcher/migrations/0005_auto_20210202_1821.py b/authentik/policies/event_matcher/migrations/0005_auto_20210202_1821.py
new file mode 100644
index 000000000..4e0b80b8d
--- /dev/null
+++ b/authentik/policies/event_matcher/migrations/0005_auto_20210202_1821.py
@@ -0,0 +1,46 @@
+# Generated by Django 3.1.6 on 2021-02-02 18:21
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("authentik_policies_event_matcher", "0004_auto_20210112_2158"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="eventmatcherpolicy",
+            name="action",
+            field=models.TextField(
+                blank=True,
+                choices=[
+                    ("login", "Login"),
+                    ("login_failed", "Login Failed"),
+                    ("logout", "Logout"),
+                    ("user_write", "User Write"),
+                    ("suspicious_request", "Suspicious Request"),
+                    ("password_set", "Password Set"),
+                    ("token_view", "Token View"),
+                    ("invitation_used", "Invite Used"),
+                    ("authorize_application", "Authorize Application"),
+                    ("source_linked", "Source Linked"),
+                    ("impersonation_started", "Impersonation Started"),
+                    ("impersonation_ended", "Impersonation Ended"),
+                    ("policy_execution", "Policy Execution"),
+                    ("policy_exception", "Policy Exception"),
+                    ("property_mapping_exception", "Property Mapping Exception"),
+                    ("system_task_execution", "System Task Execution"),
+                    ("system_task_exception", "System Task Exception"),
+                    ("configuration_error", "Configuration Error"),
+                    ("model_created", "Model Created"),
+                    ("model_updated", "Model Updated"),
+                    ("model_deleted", "Model Deleted"),
+                    ("update_available", "Update Available"),
+                    ("custom_", "Custom Prefix"),
+                ],
+                help_text="Match created events with this action type. When left empty, all action types will be matched.",
+            ),
+        ),
+    ]
diff --git a/swagger.yaml b/swagger.yaml
index bd3b20848..3f042f94d 100755
--- a/swagger.yaml
+++ b/swagger.yaml
@@ -7701,6 +7701,11 @@ definitions:
             webhook_url:
               title: Webhook url
               type: string
+            send_once:
+              title: Send once
+              description: Only send notification once, for example when sending a
+                webhook into a chat channel.
+              type: boolean
         readOnly: true
       severity:
         title: Severity