diff --git a/src/EllieBot/Db/Models/Notify.cs b/src/EllieBot/Db/Models/Notify.cs
index caa8db3..f8b29c0 100644
--- a/src/EllieBot/Db/Models/Notify.cs
+++ b/src/EllieBot/Db/Models/Notify.cs
@@ -8,7 +8,7 @@ public class Notify
     public int Id { get; set; }
 
     public ulong GuildId { get; set; }
-    public ulong ChannelId { get; set; }
+    public ulong? ChannelId { get; set; }
     public NotifyType Type { get; set; }
 
     [MaxLength(10_000)]
diff --git a/src/EllieBot/Migrations/PostgreSql/20250228044141_notify-allow-origin-channel.sql b/src/EllieBot/Migrations/PostgreSql/20250228044141_notify-allow-origin-channel.sql
new file mode 100644
index 0000000..4abfb05
--- /dev/null
+++ b/src/EllieBot/Migrations/PostgreSql/20250228044141_notify-allow-origin-channel.sql
@@ -0,0 +1,7 @@
+START TRANSACTION;
+ALTER TABLE notify ALTER COLUMN channelid DROP NOT NULL;
+
+INSERT INTO "__EFMigrationsHistory" (migrationid, productversion)
+VALUES ('20250228044141_notify-allow-origin-channel', '9.0.1');
+
+COMMIT;
diff --git a/src/EllieBot/Migrations/PostgreSql/20250226215222_init.Designer.cs b/src/EllieBot/Migrations/PostgreSql/20250228044209_init.Designer.cs
similarity index 99%
rename from src/EllieBot/Migrations/PostgreSql/20250226215222_init.Designer.cs
rename to src/EllieBot/Migrations/PostgreSql/20250228044209_init.Designer.cs
index 4bc0e7f..02ad1c2 100644
--- a/src/EllieBot/Migrations/PostgreSql/20250226215222_init.Designer.cs
+++ b/src/EllieBot/Migrations/PostgreSql/20250228044209_init.Designer.cs
@@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 namespace EllieBot.Migrations.PostgreSql
 {
     [DbContext(typeof(PostgreSqlContext))]
-    [Migration("20250226215222_init")]
+    [Migration("20250228044209_init")]
     partial class init
     {
         /// <inheritdoc />
@@ -1809,7 +1809,7 @@ namespace EllieBot.Migrations.PostgreSql
 
                 NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 
-                b.Property<decimal>("ChannelId")
+                b.Property<decimal?>("ChannelId")
                     .HasColumnType("numeric(20,0)")
                     .HasColumnName("channelid");
 
diff --git a/src/EllieBot/Migrations/PostgreSql/20250226215222_init.cs b/src/EllieBot/Migrations/PostgreSql/20250228044209_init.cs
similarity index 99%
rename from src/EllieBot/Migrations/PostgreSql/20250226215222_init.cs
rename to src/EllieBot/Migrations/PostgreSql/20250228044209_init.cs
index 9d717f0..a99bf07 100644
--- a/src/EllieBot/Migrations/PostgreSql/20250226215222_init.cs
+++ b/src/EllieBot/Migrations/PostgreSql/20250228044209_init.cs
@@ -658,7 +658,7 @@ namespace EllieBot.Migrations.PostgreSql
                     id = table.Column<int>(type: "integer", nullable: false)
                         .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
                     guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
-                    channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
+                    channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
                     type = table.Column<int>(type: "integer", nullable: false),
                     message = table.Column<string>(type: "character varying(10000)", maxLength: 10000, nullable: false)
                 },
diff --git a/src/EllieBot/Migrations/PostgreSql/PostgreSqlContextModelSnapshot.cs b/src/EllieBot/Migrations/PostgreSql/PostgreSqlContextModelSnapshot.cs
index 5c0c910..e40eb15 100644
--- a/src/EllieBot/Migrations/PostgreSql/PostgreSqlContextModelSnapshot.cs
+++ b/src/EllieBot/Migrations/PostgreSql/PostgreSqlContextModelSnapshot.cs
@@ -1806,7 +1806,7 @@ namespace EllieBot.Migrations.PostgreSql
 
                 NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 
-                b.Property<decimal>("ChannelId")
+                b.Property<decimal?>("ChannelId")
                     .HasColumnType("numeric(20,0)")
                     .HasColumnName("channelid");
 
diff --git a/src/EllieBot/Migrations/Sqlite/20250228044138_notify-allow-origin-channel.sql b/src/EllieBot/Migrations/Sqlite/20250228044138_notify-allow-origin-channel.sql
new file mode 100644
index 0000000..c010e60
--- /dev/null
+++ b/src/EllieBot/Migrations/Sqlite/20250228044138_notify-allow-origin-channel.sql
@@ -0,0 +1,29 @@
+BEGIN TRANSACTION;
+CREATE TABLE "ef_temp_Notify" (
+    "Id" INTEGER NOT NULL CONSTRAINT "PK_Notify" PRIMARY KEY AUTOINCREMENT,
+    "ChannelId" INTEGER NULL,
+    "GuildId" INTEGER NOT NULL,
+    "Message" TEXT NOT NULL,
+    "Type" INTEGER NOT NULL,
+    CONSTRAINT "AK_Notify_GuildId_Type" UNIQUE ("GuildId", "Type")
+);
+
+INSERT INTO "ef_temp_Notify" ("Id", "ChannelId", "GuildId", "Message", "Type")
+SELECT "Id", "ChannelId", "GuildId", "Message", "Type"
+FROM "Notify";
+
+COMMIT;
+
+PRAGMA foreign_keys = 0;
+
+BEGIN TRANSACTION;
+DROP TABLE "Notify";
+
+ALTER TABLE "ef_temp_Notify" RENAME TO "Notify";
+
+COMMIT;
+
+PRAGMA foreign_keys = 1;
+
+INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
+VALUES ('20250228044138_notify-allow-origin-channel', '9.0.1');
diff --git a/src/EllieBot/Migrations/Sqlite/20250226215220_init.Designer.cs b/src/EllieBot/Migrations/Sqlite/20250228044206_init.Designer.cs
similarity index 99%
rename from src/EllieBot/Migrations/Sqlite/20250226215220_init.Designer.cs
rename to src/EllieBot/Migrations/Sqlite/20250228044206_init.Designer.cs
index 18ebf28..f1d82e7 100644
--- a/src/EllieBot/Migrations/Sqlite/20250226215220_init.Designer.cs
+++ b/src/EllieBot/Migrations/Sqlite/20250228044206_init.Designer.cs
@@ -11,7 +11,7 @@ using EllieBot.Db;
 namespace EllieBot.Migrations.Sqlite
 {
     [DbContext(typeof(SqliteContext))]
-    [Migration("20250226215220_init")]
+    [Migration("20250228044206_init")]
     partial class init
     {
         /// <inheritdoc />
@@ -1351,7 +1351,7 @@ namespace EllieBot.Migrations.Sqlite
                     .ValueGeneratedOnAdd()
                     .HasColumnType("INTEGER");
 
-                b.Property<ulong>("ChannelId")
+                b.Property<ulong?>("ChannelId")
                     .HasColumnType("INTEGER");
 
                 b.Property<ulong>("GuildId")
diff --git a/src/EllieBot/Migrations/Sqlite/20250226215220_init.cs b/src/EllieBot/Migrations/Sqlite/20250228044206_init.cs
similarity index 99%
rename from src/EllieBot/Migrations/Sqlite/20250226215220_init.cs
rename to src/EllieBot/Migrations/Sqlite/20250228044206_init.cs
index 566a6e2..7e5aeba 100644
--- a/src/EllieBot/Migrations/Sqlite/20250226215220_init.cs
+++ b/src/EllieBot/Migrations/Sqlite/20250228044206_init.cs
@@ -658,7 +658,7 @@ namespace EllieBot.Migrations.Sqlite
                     Id = table.Column<int>(type: "INTEGER", nullable: false)
                         .Annotation("Sqlite:Autoincrement", true),
                     GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
-                    ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
+                    ChannelId = table.Column<ulong>(type: "INTEGER", nullable: true),
                     Type = table.Column<int>(type: "INTEGER", nullable: false),
                     Message = table.Column<string>(type: "TEXT", maxLength: 10000, nullable: false)
                 },
diff --git a/src/EllieBot/Migrations/Sqlite/SqliteContextModelSnapshot.cs b/src/EllieBot/Migrations/Sqlite/SqliteContextModelSnapshot.cs
index 6f16565..77fa0b9 100644
--- a/src/EllieBot/Migrations/Sqlite/SqliteContextModelSnapshot.cs
+++ b/src/EllieBot/Migrations/Sqlite/SqliteContextModelSnapshot.cs
@@ -1348,7 +1348,7 @@ namespace EllieBot.Migrations.Sqlite
                     .ValueGeneratedOnAdd()
                     .HasColumnType("INTEGER");
 
-                b.Property<ulong>("ChannelId")
+                b.Property<ulong?>("ChannelId")
                     .HasColumnType("INTEGER");
 
                 b.Property<ulong>("GuildId")
diff --git a/src/EllieBot/Modules/Administration/Notify/INotifyModel.cs b/src/EllieBot/Modules/Administration/Notify/INotifyModel.cs
index bbfa70d..3d241df 100644
--- a/src/EllieBot/Modules/Administration/Notify/INotifyModel.cs
+++ b/src/EllieBot/Modules/Administration/Notify/INotifyModel.cs
@@ -3,15 +3,25 @@
 namespace EllieBot.Modules.Administration;
 
 public interface INotifyModel<T>
-    where T: struct, INotifyModel<T>
+    where T : struct, INotifyModel<T>
 {
     static abstract string KeyName { get; }
     static abstract NotifyType NotifyType { get; }
     static abstract IReadOnlyList<NotifyModelPlaceholderData<T>> GetReplacements();
 
+    static virtual bool SupportsOriginTarget
+        => false;
+
     public virtual bool TryGetGuildId(out ulong guildId)
     {
         guildId = 0;
+
+        return false;
+    }
+
+    public virtual bool TryGetChannelId(out ulong channelId)
+    {
+        channelId = 0;
         return false;
     }
 
diff --git a/src/EllieBot/Modules/Administration/Notify/INotifySubscriber.cs b/src/EllieBot/Modules/Administration/Notify/INotifySubscriber.cs
index 23d4008..a1c902d 100644
--- a/src/EllieBot/Modules/Administration/Notify/INotifySubscriber.cs
+++ b/src/EllieBot/Modules/Administration/Notify/INotifySubscriber.cs
@@ -13,4 +13,7 @@ public interface INotifySubscriber
     NotifyModelData GetRegisteredModel(NotifyType nType);
 }
 
-public readonly record struct NotifyModelData(NotifyType Type, IReadOnlyList<string> Replacements);
\ No newline at end of file
+public readonly record struct NotifyModelData(
+    NotifyType Type,
+    bool SupportsOriginTarget,
+    IReadOnlyList<string> Replacements);
\ No newline at end of file
diff --git a/src/EllieBot/Modules/Administration/Notify/Models/AddRoleRewardNotifyModel.cs b/src/EllieBot/Modules/Administration/Notify/Models/AddRoleRewardNotifyModel.cs
index 60e263c..0e28d69 100644
--- a/src/EllieBot/Modules/Administration/Notify/Models/AddRoleRewardNotifyModel.cs
+++ b/src/EllieBot/Modules/Administration/Notify/Models/AddRoleRewardNotifyModel.cs
@@ -3,7 +3,12 @@ using EllieBot.Modules.Administration;
 
 namespace EllieBot.Modules.Xp.Services;
 
-public record struct AddRoleRewardNotifyModel(ulong GuildId, ulong RoleId, ulong UserId, long Level) : INotifyModel<AddRoleRewardNotifyModel>
+public record struct AddRoleRewardNotifyModel(
+    ulong GuildId,
+    ulong RoleId,
+    ulong UserId,
+    long Level)
+    : INotifyModel<AddRoleRewardNotifyModel>
 {
     public static string KeyName
         => "notify.reward.addrole";
@@ -18,9 +23,9 @@ public record struct AddRoleRewardNotifyModel(ulong GuildId, ulong RoleId, ulong
     public static IReadOnlyList<NotifyModelPlaceholderData<AddRoleRewardNotifyModel>> GetReplacements()
         =>
         [
-            new(PH_LEVEL, static (data, g) => data.Level.ToString() ),
-            new(PH_USER, static (data, g) => g.GetUser(data.UserId)?.ToString() ?? data.UserId.ToString() ),
-            new(PH_ROLE, static (data, g) => g.GetRole(data.RoleId)?.ToString() ?? data.RoleId.ToString() )
+            new(PH_LEVEL, static (data, g) => data.Level.ToString()),
+            new(PH_USER, static (data, g) => g.GetUser(data.UserId)?.ToString() ?? data.UserId.ToString()),
+            new(PH_ROLE, static (data, g) => g.GetRole(data.RoleId)?.ToString() ?? data.RoleId.ToString())
         ];
 
     public bool TryGetUserId(out ulong userId)
diff --git a/src/EllieBot/Modules/Administration/Notify/Models/LevelUpNotifyModel.cs b/src/EllieBot/Modules/Administration/Notify/Models/LevelUpNotifyModel.cs
index 2643e22..cdbd89e 100644
--- a/src/EllieBot/Modules/Administration/Notify/Models/LevelUpNotifyModel.cs
+++ b/src/EllieBot/Modules/Administration/Notify/Models/LevelUpNotifyModel.cs
@@ -4,7 +4,7 @@ namespace EllieBot.Modules.Administration;
 
 public readonly record struct LevelUpNotifyModel(
     ulong GuildId,
-    ulong ChannelId,
+    ulong? ChannelId,
     ulong UserId,
     long Level) : INotifyModel<LevelUpNotifyModel>
 {
@@ -21,17 +21,32 @@ public readonly record struct LevelUpNotifyModel(
     {
         return
         [
-            new(PH_LEVEL, static (data, g) => data.Level.ToString() ),
-            new(PH_USER, static (data, g) => g.GetUser(data.UserId)?.ToString() ?? data.UserId.ToString() )
+            new(PH_LEVEL, static (data, g) => data.Level.ToString()),
+            new(PH_USER, static (data, g) => g.GetUser(data.UserId)?.ToString() ?? data.UserId.ToString())
         ];
     }
 
+    public static bool SupportsOriginTarget
+        => true;
+
     public readonly bool TryGetGuildId(out ulong guildId)
     {
         guildId = GuildId;
         return true;
     }
 
+    public readonly bool TryGetChannelId(out ulong channelId)
+    {
+        if (ChannelId is ulong cid)
+        {
+            channelId = cid;
+            return true;
+        }
+
+        channelId = 0;
+        return false;
+    }
+
     public readonly bool TryGetUserId(out ulong userId)
     {
         userId = UserId;
diff --git a/src/EllieBot/Modules/Administration/Notify/NotifyCommands.cs b/src/EllieBot/Modules/Administration/Notify/NotifyCommands.cs
index 8c7e2de..55dc1f9 100644
--- a/src/EllieBot/Modules/Administration/Notify/NotifyCommands.cs
+++ b/src/EllieBot/Modules/Administration/Notify/NotifyCommands.cs
@@ -12,23 +12,23 @@ public partial class Administration
         public async Task Notify()
         {
             await Response()
-                  .Paginated()
-                  .Items(Enum.GetValues<NotifyType>().DistinctBy(x => (int)x).ToList())
-                  .PageSize(5)
-                  .Page((items, page) =>
-                  {
-                      var eb = CreateEmbed()
-                               .WithOkColor()
-                               .WithTitle(GetText(strs.notify_available));
+                .Paginated()
+                .Items(Enum.GetValues<NotifyType>().DistinctBy(x => (int)x).ToList())
+                .PageSize(5)
+                .Page((items, page) =>
+                {
+                    var eb = CreateEmbed()
+                        .WithOkColor()
+                        .WithTitle(GetText(strs.notify_available));
 
-                      foreach (var item in items)
-                      {
-                          eb.AddField(item.ToString(), GetText(GetDescription(item)), false);
-                      }
+                    foreach (var item in items)
+                    {
+                        eb.AddField(item.ToString(), GetText(GetDescription(item)), false);
+                    }
 
-                      return eb;
-                  })
-                  .SendAsync();
+                    return eb;
+                })
+                .SendAsync();
         }
 
         private LocStr GetDescription(NotifyType item)
@@ -43,36 +43,56 @@ public partial class Administration
 
         [Cmd]
         [UserPerm(GuildPerm.Administrator)]
-        public async Task Notify(NotifyType nType, [Leftover] string? message = null)
+        public async Task Notify(NotifyType nType)
         {
-            if (string.IsNullOrWhiteSpace(message))
+            // show msg 
+            var conf = await _service.GetNotifyAsync(ctx.Guild.Id, nType);
+            if (conf is null)
             {
-                // show msg 
-                var conf = await _service.GetNotifyAsync(ctx.Guild.Id, nType);
-                if (conf is null)
-                {
-                    await Response().Confirm(strs.notify_msg_not_set).SendAsync();
-                    return;
-                }
-
-                var eb = CreateEmbed()
-                         .WithOkColor()
-                         .WithTitle(GetText(strs.notify_msg))
-                         .WithDescription(conf.Message.TrimTo(2048))
-                         .AddField(GetText(strs.notify_type), conf.Type.ToString(), true)
-                         .AddField(GetText(strs.channel),
-                             $"""
-                              <#{conf.ChannelId}>
-                              `{conf.ChannelId}`
-                              """,
-                             true);
-
-                await Response().Embed(eb).SendAsync();
+                await Response().Confirm(strs.notify_msg_not_set).SendAsync();
                 return;
             }
 
-            await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nType, message);
-            await Response().Confirm(strs.notify_on($"<#{ctx.Channel.Id}>", Format.Bold(nType.ToString()))).SendAsync();
+            var outChannel = conf.ChannelId is null
+                ? """
+                  from which the event originated
+                  `origin`
+                  """
+                : $"""
+                   <#{conf.ChannelId}>
+                   `{conf.ChannelId}`
+                   """;
+            var eb = CreateEmbed()
+                .WithOkColor()
+                .WithTitle(GetText(strs.notify_msg))
+                .WithDescription(conf.Message.TrimTo(2048))
+                .AddField(GetText(strs.notify_type), conf.Type.ToString(), true)
+                .AddField(GetText(strs.channel),
+                    outChannel,
+                    true);
+
+            await Response().Embed(eb).SendAsync();
+            return;
+        }
+
+        [Cmd]
+        [UserPerm(GuildPerm.Administrator)]
+        public async Task Notify(NotifyType nType, [Leftover] string message)
+            => await NotifyInternalAsync(nType, null, message);
+
+        [Cmd]
+        [UserPerm(GuildPerm.Administrator)]
+        public async Task Notify(NotifyType nType, IMessageChannel channel, [Leftover] string message)
+            => await NotifyInternalAsync(nType, channel, message);
+
+        private async Task NotifyInternalAsync(NotifyType nType, IMessageChannel? channel, [Leftover] string message)
+        {
+            var result = await _service.EnableAsync(ctx.Guild.Id, channel?.Id, nType, message);
+
+            var outChannel = channel is null ? "origin" : $"<#{channel.Id}>";
+            await Response()
+                .Confirm(strs.notify_on(outChannel, Format.Bold(nType.ToString())))
+                .SendAsync();
         }
 
         [Cmd]
@@ -82,13 +102,12 @@ public partial class Administration
             var data = _service.GetRegisteredModel(nType);
 
             var eb = CreateEmbed()
-                     .WithOkColor()
-                     .WithTitle(GetText(strs.notify_placeholders(nType.ToString().ToLower())));
+                .WithOkColor()
+                .WithTitle(GetText(strs.notify_placeholders(nType.ToString().ToLower())));
 
             eb.WithDescription(data.Replacements.Join("\n---\n", x => $"`%event.{x}%`"));
 
             await Response().Embed(eb).SendAsync();
-
         }
 
         [Cmd]
@@ -115,8 +134,8 @@ public partial class Administration
                 sb.AppendLine(GetText(strs.notify_none));
 
             await Response()
-                  .Confirm(GetText(strs.notify_list), text: sb.ToString())
-                  .SendAsync();
+                .Confirm(GetText(strs.notify_list), text: sb.ToString())
+                .SendAsync();
         }
 
         [Cmd]
diff --git a/src/EllieBot/Modules/Administration/Notify/NotifyService.cs b/src/EllieBot/Modules/Administration/Notify/NotifyService.cs
index 9fc7ea7..d23d71d 100644
--- a/src/EllieBot/Modules/Administration/Notify/NotifyService.cs
+++ b/src/EllieBot/Modules/Administration/Notify/NotifyService.cs
@@ -16,6 +16,7 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
     private readonly IBotCreds _creds;
     private readonly IReplacementService _repSvc;
     private readonly IPubSub _pubSub;
+
     private ConcurrentDictionary<NotifyType, ConcurrentDictionary<ulong, Notify>> _events = new();
 
     public NotifyService(
@@ -36,12 +37,10 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
 
     private void RegisterModels()
     {
-
         RegisterModel<LevelUpNotifyModel>();
         RegisterModel<ProtectionNotifyModel>();
         RegisterModel<AddRoleRewardNotifyModel>();
         RegisterModel<RemoveRoleRewardNotifyModel>();
-
     }
 
     public async Task OnReadyAsync()
@@ -84,7 +83,7 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
         catch (Exception ex)
         {
             Log.Warning(ex,
-                "Unknown error occurred while trying to triger {NotifyEvent} for {NotifyModel}",
+                "Unknown error occurred while trying to trigger {NotifyEvent} for {NotifyModel}",
                 T.KeyName,
                 data);
         }
@@ -93,36 +92,39 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
     private async Task OnEvent<T>(T model)
         where T : struct, INotifyModel<T>
     {
-        if (_events.TryGetValue(T.NotifyType, out var subs))
+        if (!_events.TryGetValue(T.NotifyType, out var subs))
+            return;
+
+        // make sure the event is consumed
+        // only in the guild it was meant for
+        if (model.TryGetGuildId(out var gid))
         {
-            if (model.TryGetGuildId(out var gid))
-            {
-                if (!subs.TryGetValue(gid, out var conf))
-                    return;
-
-                await HandleNotifyEvent(conf, model);
+            if (!subs.TryGetValue(gid, out var conf))
                 return;
-            }
 
-            foreach (var key in subs.Keys.ToArray())
+            await HandleNotifyEvent(conf, model);
+            return;
+        }
+
+        // todo optimize this
+        foreach (var key in subs.Keys)
+        {
+            if (subs.TryGetValue(key, out var notif))
             {
-                if (subs.TryGetValue(key, out var notif))
+                try
                 {
-                    try
-                    {
-                        await HandleNotifyEvent(notif, model);
-                    }
-                    catch (Exception ex)
-                    {
-                        Log.Error(ex,
-                            "Error occured while sending notification {NotifyEvent} to guild {GuildId}: {ErrorMessage}",
-                            T.NotifyType,
-                            key,
-                            ex.Message);
-                    }
-
-                    await Task.Delay(500);
+                    await HandleNotifyEvent(notif, model);
                 }
+                catch (Exception ex)
+                {
+                    Log.Error(ex,
+                        "Error occured while sending notification {NotifyEvent} to guild {GuildId}: {ErrorMessage}",
+                        T.NotifyType,
+                        key,
+                        ex.Message);
+                }
+
+                await Task.Delay(500);
             }
         }
     }
@@ -131,9 +133,27 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
         where T : struct, INotifyModel<T>
     {
         var guild = _client.GetGuild(conf.GuildId);
-        var channel = guild?.GetTextChannel(conf.ChannelId);
 
-        if (guild is null || channel is null)
+        // bot probably left the guild, cleanup?
+        if (guild is null)
+            return;
+
+        IMessageChannel? channel;
+        // if notify channel is specified for this event, send the event to that channel
+        if (conf.ChannelId is ulong confCid)
+        {
+            channel = guild.GetTextChannel(confCid);
+        }
+        else
+        {
+            // otherwise get the origin channel of the event
+            if (!model.TryGetChannelId(out var cid))
+                return;
+
+            channel = guild.GetChannel(cid) as IMessageChannel;
+        }
+
+        if (channel is null)
             return;
 
         IUser? user = null;
@@ -165,14 +185,24 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
             .SendAsync();
     }
 
-    private static string GetPhToken(string name) => $"%event.{name}%";
+    private static string GetPhToken(string name)
+        => $"%event.{name}%";
 
-    public async Task EnableAsync(
+    public async Task<bool> EnableAsync(
         ulong guildId,
-        ulong channelId,
+        ulong? channelId,
         NotifyType nType,
         string message)
     {
+        // check if the notify type model supports null channel
+        if (channelId is null)
+        {
+            var model = GetRegisteredModel(nType);
+            if (!model.SupportsOriginTarget)
+                return false;
+        }
+
+
         await using var uow = _db.GetDbContext();
         await uow.GetTable<Notify>()
             .InsertOrUpdateAsync(() => new()
@@ -201,6 +231,8 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
             Type = nType,
             Message = message
         };
+
+        return true;
     }
 
     public async Task DisableAsync(ulong guildId, NotifyType nType)
@@ -244,11 +276,15 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
 
     // messed up big time, it was supposed to be fully extensible, but it's stored as an enum in the database already...
     private readonly ConcurrentDictionary<NotifyType, NotifyModelData> _models = new();
+
     public void RegisterModel<T>() where T : struct, INotifyModel<T>
     {
-        var data = new NotifyModelData(T.NotifyType, T.GetReplacements().Map(x => x.Name));
+        var data = new NotifyModelData(T.NotifyType,
+            T.SupportsOriginTarget,
+            T.GetReplacements().Map(x => x.Name));
         _models[T.NotifyType] = data;
     }
 
-    public NotifyModelData GetRegisteredModel(NotifyType nType) => _models[nType];
-}
+    public NotifyModelData GetRegisteredModel(NotifyType nType)
+        => _models[nType];
+}
\ No newline at end of file
diff --git a/src/EllieBot/Modules/Xp/XpRate/GuildConfigXpService.cs b/src/EllieBot/Modules/Xp/XpRate/GuildConfigXpService.cs
index acf7fbb..9b08527 100644
--- a/src/EllieBot/Modules/Xp/XpRate/GuildConfigXpService.cs
+++ b/src/EllieBot/Modules/Xp/XpRate/GuildConfigXpService.cs
@@ -85,6 +85,8 @@ public class GuildConfigXpService(DbService db, ShardData shardData, XpConfigSer
                     GuildId = guildId,
                     RateType = type,
                 });
+
+        _guildRates[(type, guildId)] = new XpRate(type, amount, cooldown);
     }
 
     public async Task SetChannelXpRateAsync(ulong guildId,
@@ -119,6 +121,9 @@ public class GuildConfigXpService(DbService db, ShardData shardData, XpConfigSer
                     ChannelId = channelId,
                     RateType = type,
                 });
+
+        _channelRates.GetOrAdd(guildId, _ => new())
+            [(type, channelId)] = new XpRate(type, amount, cooldown);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -137,6 +142,9 @@ public class GuildConfigXpService(DbService db, ShardData shardData, XpConfigSer
         var deleted = await uow.GetTable<GuildXpConfig>()
             .Where(x => x.GuildId == guildId)
             .DeleteAsync();
+
+        _guildRates.TryRemove((XpRateType.Text, guildId), out _);
+
         return deleted > 0;
     }
 
@@ -146,6 +154,10 @@ public class GuildConfigXpService(DbService db, ShardData shardData, XpConfigSer
         var deleted = await uow.GetTable<ChannelXpConfig>()
             .Where(x => x.GuildId == guildId && x.ChannelId == channelId)
             .DeleteAsync();
+
+        if (_channelRates.TryGetValue(guildId, out var channelRates))
+            channelRates.TryRemove((XpRateType.Text, channelId), out _);
+
         return deleted > 0;
     }
 
diff --git a/src/EllieBot/Modules/Xp/XpService.cs b/src/EllieBot/Modules/Xp/XpService.cs
index 5b0b6b5..431f92c 100644
--- a/src/EllieBot/Modules/Xp/XpService.cs
+++ b/src/EllieBot/Modules/Xp/XpService.cs
@@ -155,7 +155,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
 
                     if (oldBatch.Contains(u))
                     {
-                        validUsers.Add(new(u, rate.Amount));
+                        validUsers.Add(new(u, rate.Amount, vc.Id));
                     }
 
                     _voiceXpBatch.Add(u);
@@ -218,13 +218,13 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
                 (batch, stats) => stats)
             .ToListAsyncLinqToDB();
 
-        var userToXp = currentBatch.ToDictionary(x => x.User.Id, x => x.Xp);
+        var userToXp = currentBatch.ToDictionary(x => x.User.Id, x => x);
         foreach (var u in updated)
         {
-            if (!userToXp.TryGetValue(u.UserId, out var xpGained))
+            if (!userToXp.TryGetValue(u.UserId, out var data))
                 continue;
 
-            var oldStats = new LevelStats(u.Xp - xpGained);
+            var oldStats = new LevelStats(u.Xp - data.Xp);
             var newStats = new LevelStats(u.Xp);
 
             Log.Information("User {User} xp updated from {OldLevel} to {NewLevel}",
@@ -235,9 +235,8 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
             if (oldStats.Level < newStats.Level)
             {
                 await _levelUpQueue.EnqueueAsync(NotifyUser(u.GuildId,
-                    0,
+                    data.ChannelId,
                     u.UserId,
-                    true,
                     oldStats.Level,
                     newStats.Level));
             }
@@ -246,19 +245,15 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
 
     private Func<Task> NotifyUser(
         ulong guildId,
-        ulong channelId,
+        ulong? channelId,
         ulong userId,
-        bool isServer,
         long oldLevel,
         long newLevel)
         => async () =>
         {
-            if (isServer)
-            {
-                await HandleRewardsInternalAsync(guildId, userId, oldLevel, newLevel);
-            }
+            await HandleRewardsInternalAsync(guildId, userId, oldLevel, newLevel);
 
-            await HandleNotifyInternalAsync(guildId, channelId, userId, isServer, newLevel);
+            await HandleNotifyInternalAsync(guildId, channelId, userId, newLevel);
         };
 
     private async Task HandleRewardsInternalAsync(
@@ -338,9 +333,8 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
 
     private async Task HandleNotifyInternalAsync(
         ulong guildId,
-        ulong channelId,
+        ulong? channelId,
         ulong userId,
-        bool isServer,
         long newLevel)
     {
         var guild = _client.GetGuild(guildId);
@@ -349,18 +343,15 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
         if (guild is null || user is null)
             return;
 
-        if (isServer)
+        var model = new LevelUpNotifyModel()
         {
-            var model = new LevelUpNotifyModel()
-            {
-                GuildId = guildId,
-                UserId = userId,
-                ChannelId = channelId,
-                Level = newLevel
-            };
-            await _notifySub.NotifyAsync(model, true);
-            return;
-        }
+            GuildId = guildId,
+            UserId = userId,
+            ChannelId = channelId,
+            Level = newLevel
+        };
+        await _notifySub.NotifyAsync(model, true);
+        return;
     }
 
     public async Task SetCurrencyReward(ulong guildId, int level, int amount)
@@ -552,7 +543,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
             if (!await TryAddUserGainedXpAsync(user.Id, rate.Cooldown))
                 return;
 
-            _usersBatch.Add(new(user, rate.Amount));
+            _usersBatch.Add(new(user, rate.Amount, gc.Id));
         });
 
         return Task.CompletedTask;
@@ -1169,7 +1160,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
     }
 }
 
-public readonly record struct XpQueueEntry(IGuildUser User, long Xp)
+public readonly record struct XpQueueEntry(IGuildUser User, long Xp, ulong? ChannelId)
 {
     public bool Equals(XpQueueEntry? other)
         => other?.User == User;
diff --git a/src/EllieBot/data/patron.yml b/src/EllieBot/data/patron.yml
index 632a3e6..2d2a3b9 100644
--- a/src/EllieBot/data/patron.yml
+++ b/src/EllieBot/data/patron.yml
@@ -4,16 +4,6 @@ version: 3
 isEnabled: false
 # Who can do how much of what
 limits:
-  100:
-    ChatBot:
-      quota: 50000000
-      quotaPeriod: PerMonth
-    ReactionRole:
-      quota: -1
-      quotaPeriod: Total
-    Prune:
-      quota: -1
-      quotaPeriod: PerDay
   50:
     ChatBot:
       quota: 20000000
@@ -24,16 +14,6 @@ limits:
     Prune:
       quota: -1
       quotaPeriod: PerDay
-  20:
-    ChatBot:
-      quota: 6500000
-      quotaPeriod: PerMonth
-    ReactionRole:
-      quota: -1
-      quotaPeriod: Total
-    Prune:
-      quota: 20
-      quotaPeriod: PerDay
   10:
     ChatBot:
       quota: 2500000
@@ -44,7 +24,7 @@ limits:
     Prune:
       quota: 5
       quotaPeriod: PerDay
-  5:
+  2:
     ChatBot:
       quota: 1000000
       quotaPeriod: PerMonth
@@ -53,4 +33,4 @@ limits:
       quotaPeriod: Total
     Prune:
       quota: 2
-      quotaPeriod: PerDay
+      quotaPeriod: PerDay
\ No newline at end of file
diff --git a/src/EllieBot/strings/commands/commands.en-US.yml b/src/EllieBot/strings/commands/commands.en-US.yml
index 527679c..51077ef 100644
--- a/src/EllieBot/strings/commands/commands.en-US.yml
+++ b/src/EllieBot/strings/commands/commands.en-US.yml
@@ -4862,14 +4862,18 @@ minesweeper:
         desc: "The number of mines to create."
 notify:
   desc: |-
-    Sends a message to the current channel once the specified event occurs.
+    Sends a message to the specified channel once the specified event occurs.
+
+    If no channel is specified, the message will be sent to the channel from which the event originated.
+    *note: this is only possible for events that have an origin channel (for example `levelup`)*
+
     Provide no parameters to see all available events.
   ex:
     - 'levelup Congratulations to user %user.name% for reaching level %event.level%'
   params:
     - { }
     - event:
-        desc: "The event to notify on."
+        desc: "The event for which to see the current message."
     - event:
         desc: "The event to notify on."
       message: