diff --git a/App/App.xaml.cs b/App/App.xaml.cs
index 2c7e87e..2cdee97 100644
--- a/App/App.xaml.cs
+++ b/App/App.xaml.cs
@@ -21,6 +21,7 @@
 using Microsoft.Windows.AppLifecycle;
 using Serilog;
 using LaunchActivatedEventArgs = Microsoft.UI.Xaml.LaunchActivatedEventArgs;
+using Microsoft.Windows.AppNotifications;
 
 namespace Coder.Desktop.App;
 
@@ -70,6 +71,7 @@ public App()
         services.AddOptions<MutagenControllerConfig>()
             .Bind(builder.Configuration.GetSection(MutagenControllerConfigSection));
         services.AddSingleton<ISyncSessionController, MutagenController>();
+        services.AddSingleton<IUserNotifier, UserNotifier>();
 
         // SignInWindow views and view models
         services.AddTransient<SignInViewModel>();
@@ -188,10 +190,14 @@ public void OnActivated(object? sender, AppActivationArguments args)
                     _logger.LogWarning("URI activation with null data");
                     return;
                 }
-
                 HandleURIActivation(protoArgs.Uri);
                 break;
 
+            case ExtendedActivationKind.AppNotification:
+                var notificationArgs = (args.Data as AppNotificationActivatedEventArgs)!;
+                HandleNotification(null, notificationArgs);
+                break;
+
             default:
                 _logger.LogWarning("activation for {kind}, which is unhandled", args.Kind);
                 break;
@@ -204,6 +210,12 @@ public void HandleURIActivation(Uri uri)
         _logger.LogInformation("handling URI activation for {path}", uri.AbsolutePath);
     }
 
+    public void HandleNotification(AppNotificationManager? sender, AppNotificationActivatedEventArgs args)
+    {
+        // right now, we don't do anything other than log
+        _logger.LogInformation("handled notification activation");
+    }
+
     private static void AddDefaultConfig(IConfigurationBuilder builder)
     {
         var logPath = Path.Combine(
diff --git a/App/Program.cs b/App/Program.cs
index 1a54b2b..3749c3b 100644
--- a/App/Program.cs
+++ b/App/Program.cs
@@ -5,6 +5,7 @@
 using Microsoft.UI.Dispatching;
 using Microsoft.UI.Xaml;
 using Microsoft.Windows.AppLifecycle;
+using Microsoft.Windows.AppNotifications;
 using WinRT;
 
 namespace Coder.Desktop.App;
@@ -28,9 +29,9 @@ private static void Main(string[] args)
         {
             ComWrappersSupport.InitializeComWrappers();
             var mainInstance = GetMainInstance();
+            var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
             if (!mainInstance.IsCurrent)
             {
-                var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
                 mainInstance.RedirectActivationToAsync(activationArgs).AsTask().Wait();
                 return;
             }
@@ -58,6 +59,15 @@ private static void Main(string[] args)
 
                 // redirections via RedirectActivationToAsync above get routed to the App
                 mainInstance.Activated += app.OnActivated;
+                var notificationManager = AppNotificationManager.Default;
+                notificationManager.NotificationInvoked += app.HandleNotification;
+                notificationManager.Register();
+                if (activationArgs.Kind != ExtendedActivationKind.Launch)
+                {
+                    // this means we were activated without having already launched, so handle
+                    // the activation as well.
+                    app.OnActivated(null, activationArgs);
+                }
             });
         }
         catch (Exception e)
diff --git a/App/Services/UserNotifier.cs b/App/Services/UserNotifier.cs
new file mode 100644
index 0000000..9cdf6c1
--- /dev/null
+++ b/App/Services/UserNotifier.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.Windows.AppNotifications;
+using Microsoft.Windows.AppNotifications.Builder;
+
+namespace Coder.Desktop.App.Services;
+
+public interface IUserNotifier : IAsyncDisposable
+{
+    public Task ShowErrorNotification(string title, string message);
+}
+
+public class UserNotifier : IUserNotifier
+{
+    private readonly AppNotificationManager _notificationManager = AppNotificationManager.Default;
+
+    public ValueTask DisposeAsync()
+    {
+        return ValueTask.CompletedTask;
+    }
+
+    public Task ShowErrorNotification(string title, string message)
+    {
+        var builder = new AppNotificationBuilder().AddText(title).AddText(message);
+        _notificationManager.Show(builder.BuildNotification());
+        return Task.CompletedTask;
+    }
+}
+