diff --git a/include/SDL3/SDL.h b/include/SDL3/SDL.h
index cb51cd7ac2f8a4..337af08909d62a 100644
--- a/include/SDL3/SDL.h
+++ b/include/SDL3/SDL.h
@@ -58,6 +58,7 @@
 #include <SDL3/SDL_misc.h>
 #include <SDL3/SDL_mouse.h>
 #include <SDL3/SDL_mutex.h>
+#include <SDL3/SDL_notification.h>
 #include <SDL3/SDL_pixels.h>
 #include <SDL3/SDL_platform.h>
 #include <SDL3/SDL_power.h>
diff --git a/include/SDL3/SDL_notification.h b/include/SDL3/SDL_notification.h
new file mode 100644
index 00000000000000..4aeea8d4f17bee
--- /dev/null
+++ b/include/SDL3/SDL_notification.h
@@ -0,0 +1,113 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/**
+ *  \file SDL_notification.h
+ *
+ *  \brief Header file for notification API
+ */
+
+#ifndef SDL_notification_h_
+#define SDL_notification_h_
+
+#include <SDL3/SDL_stdinc.h>
+
+#include <SDL3/SDL_begin_code.h>
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief SDL_Notification flags.
+ */
+typedef enum
+{
+    SDL_NOTIFICATION_PRIORITY_LOW        = 0x00000010,   /**< lowest */
+    SDL_NOTIFICATION_PRIORITY_NORMAL     = 0x00000020,   /**< normal/medium */
+    SDL_NOTIFICATION_PRIORITY_HIGH       = 0x00000040    /**< high/important/critical */
+} SDL_NotificationFlags;
+
+/**
+ * \brief SDL_Icon flags.
+ */
+typedef enum
+{
+    SDL_ICON_TYPE_SINGLE_FILE    = 0x00000010,   /**< A single icon file. */
+    SDL_ICON_TYPE_SURFACE        = 0x00000020,   /**< Icon inside an SDL surface. */
+    SDL_ICON_TYPE_WINDOW         = 0x00000040    /**< Icon is same as SDL window. */
+} SDL_IconFlags;
+
+/**
+ * Notification structure containing title, text, window, etc.
+ */
+typedef struct
+{
+    Uint32 flags;                       /**< ::SDL_NotificationFlags */
+    const char *title;                  /**< UTF-8 title */
+    const char *message;                /**< UTF-8 message text */
+
+    struct
+    {
+        Uint32 flags;                       /**< ::SDL_IconFlags */
+        union {
+            const char *path;
+            SDL_Surface *surface;
+            SDL_Window *window;
+        } data;
+    } icon;
+
+} SDL_NotificationData;
+
+/**
+ *  \brief Create a system notification.
+ *
+ *  \param notificationdata the SDL_NotificationData structure with title, text and other options
+ *  \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ *  \sa SDL_ShowSimpleNotification
+ *  \sa SDL_NotificationData
+ */
+extern DECLSPEC int SDLCALL SDL_ShowNotification(const SDL_NotificationData *notificationdata);
+
+/**
+ *  \brief Create a simple system notification.
+ *
+ *  \param title    UTF-8 title text
+ *  \param message  UTF-8 message text
+ *  \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ *  \sa SDL_ShowNotification
+ *  \sa SDL_NotificationData
+ */
+extern DECLSPEC int SDLCALL SDL_ShowSimpleNotification(const char *title, const char *message);
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+#include <SDL3/SDL_close_code.h>
+
+
+#endif /* SDL_notification_h_ */
+
diff --git a/src/core/linux/SDL_dbus.c b/src/core/linux/SDL_dbus.c
index 16a7c13f94e886..8e755e12145a18 100644
--- a/src/core/linux/SDL_dbus.c
+++ b/src/core/linux/SDL_dbus.c
@@ -498,4 +498,63 @@ SDL_DBus_ScreensaverInhibit(SDL_bool inhibit)
 
     return SDL_TRUE;
 }
+
+int SDL_DBus_ShowNotification(const SDL_NotificationData *notificationdata)
+{
+    dbus_bool_t status = 0;
+    DBusConnection *conn = dbus.session_conn;
+    DBusMessage *msg = NULL;
+    Uint32 replace_id = 0;
+    Sint32 timeout = -1;
+    const char *icon = "";
+    DBusMessageIter iter;
+    DBusMessageIter sub;
+
+    const char *app;
+    const char *title;
+    const char *body;
+
+
+    app = notificationdata->title;
+    title = notificationdata->title;
+    body = notificationdata->message;
+
+    if (conn == NULL) {
+        return SDL_FALSE;
+    }
+
+    msg = dbus.message_new_method_call("org.freedesktop.Notifications",
+                                       "/org/freedesktop/Notifications",
+                                       "org.freedesktop.Notifications",
+                                       "Notify");
+    if (msg != NULL) {
+        dbus.message_iter_init_append(msg, &iter);
+        status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_STRING, &app);
+        status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &replace_id);
+        status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_STRING, &icon);
+        status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_STRING, &title);
+        status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_STRING, &body);
+        status = dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub);
+        status = dbus.message_iter_close_container(&iter, &sub);
+        status = dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub);
+        status = dbus.message_iter_close_container(&iter, &sub);
+        status = dbus.message_iter_append_basic(&iter, DBUS_TYPE_INT32, &timeout);
+        if (!status) {
+            SDL_SetError("Error appending to dbus parameters (notification)");
+        } else {
+            status = dbus.connection_send(conn, msg, NULL);
+            if (!status) {
+                SDL_SetError("Error sending to dbus (notification)");
+            } else {
+                dbus.connection_flush(conn);
+            }
+        }
+
+        dbus.message_unref(msg);
+    }
+
+    return (status) ? SDL_TRUE : SDL_FALSE;
+}
+
+
 #endif
diff --git a/src/core/linux/SDL_dbus.h b/src/core/linux/SDL_dbus.h
index 2a90e0a21c0445..02a0476dd00fa6 100644
--- a/src/core/linux/SDL_dbus.h
+++ b/src/core/linux/SDL_dbus.h
@@ -93,6 +93,7 @@ extern SDL_bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const c
 
 extern void SDL_DBus_ScreensaverTickle(void);
 extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit);
+extern int SDL_DBus_ShowNotification(const SDL_NotificationData *notificationdata);
 
 #endif /* HAVE_DBUS_DBUS_H */
 
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 4b334c254cb3d5..be7bb2aebff31f 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -841,6 +841,8 @@ SDL3_0.0.0 {
     SDL_GetSystemTheme;
     SDL_CreatePopupWindow;
     SDL_GetWindowParent;
+    SDL_ShowNotification;
+    SDL_ShowSimpleNotification;
     # extra symbols go here (don't modify this line)
   local: *;
 };
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 0d4b3c9c7954f2..37364545ade079 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -868,3 +868,5 @@
 #define SDL_GetSystemTheme SDL_GetSystemTheme_REAL
 #define SDL_CreatePopupWindow SDL_CreatePopupWindow_REAL
 #define SDL_GetWindowParent SDL_GetWindowParent_REAL
+#define SDL_ShowNotification SDL_ShowNotification_REAL
+#define SDL_ShowSimpleNotification SDL_ShowSimpleNotification_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 8945a153515b48..1980ce4a14eaf0 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -913,3 +913,5 @@ SDL_DYNAPI_PROC(int,SDL_GetRenderWindowSize,(SDL_Renderer *a, int *b, int *c),(a
 SDL_DYNAPI_PROC(SDL_SystemTheme,SDL_GetSystemTheme,(void),(),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_CreatePopupWindow,(SDL_Window *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_GetWindowParent,(SDL_Window *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_ShowNotification,(const SDL_NotificationData *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_ShowSimpleNotification,(const char *a, const char *b),(a,b),return)
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index b256f2744728af..52841dde6ce3e0 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -344,6 +344,9 @@ struct SDL_VideoDevice
     /* Hit-testing */
     int (*SetWindowHitTest)(SDL_Window *window, SDL_bool enabled);
 
+    /* Notification */
+    int (*ShowNotification)(_THIS, const SDL_NotificationData *notificationdata);
+
     /* Tell window that app enabled drag'n'drop events */
     void (*AcceptDragAndDrop)(SDL_Window *window, SDL_bool accept);
 
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 705ce074f1e5dd..33017a595c6b05 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -5045,3 +5045,46 @@ void *SDL_Metal_GetLayer(SDL_MetalView view)
         return NULL;
     }
 }
+
+int SDL_ShowNotification(const SDL_NotificationData *notificationdata)
+{
+    if (notificationdata == NULL) {
+        return SDL_InvalidParamError("notificationdata");
+    }
+
+    if (notificationdata->title == NULL) {
+        return SDL_InvalidParamError("notificationdata->title");
+    }
+
+    if (notificationdata->message == NULL) {
+        return SDL_InvalidParamError("notificationdata->message");
+    }
+
+    if (notificationdata->icon.flags == SDL_ICON_TYPE_SINGLE_FILE && notificationdata->icon.data.path == NULL) {
+        return SDL_InvalidParamError("notificationdata->icon.dta.path");
+    }
+
+    if (notificationdata->icon.flags == SDL_ICON_TYPE_SINGLE_FILE && notificationdata->icon.data.surface == NULL) {
+        return SDL_InvalidParamError("notificationdata->icon.data.surface");
+    }
+
+    if (notificationdata->icon.flags == SDL_ICON_TYPE_WINDOW && notificationdata->icon.data.window == NULL) {
+        return SDL_InvalidParamError("notificationdata->icon.data.window");
+    }
+
+    if (_this && _this->ShowNotification) {
+        return _this->ShowNotification(_this, notificationdata);
+    }
+
+    return SDL_Unsupported();
+}
+
+
+int SDL_ShowSimpleNotification(const char *title, const char *message)
+{
+    SDL_NotificationData notificationdata;
+    SDL_zero(notificationdata);
+    notificationdata.title = title;
+    notificationdata.message = message;
+    return SDL_ShowNotification(&notificationdata);
+}
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 1f42324e0a56a7..441da0abdd35cc 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -162,6 +162,12 @@ static void Wayland_DeleteDevice(SDL_VideoDevice *device)
     SDL_WAYLAND_UnloadSymbols();
 }
 
+#ifdef SDL_USE_LIBDBUS
+static int Wayland_ShowNotification(_THIS, const SDL_NotificationData *notificationdata) {
+    return SDL_DBus_ShowNotification(notificationdata);
+}
+#endif
+
 static SDL_VideoDevice *Wayland_CreateDevice(void)
 {
     SDL_VideoDevice *device;
@@ -266,6 +272,10 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
     device->StopTextInput = Wayland_StopTextInput;
     device->SetTextInputRect = Wayland_SetTextInputRect;
 
+#ifdef SDL_USE_LIBDBUS
+    device->ShowNotification = Wayland_ShowNotification;
+#endif
+
 #if SDL_VIDEO_VULKAN
     device->Vulkan_LoadLibrary = Wayland_Vulkan_LoadLibrary;
     device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary;
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index 3d182b2ba57f1d..a464efc0e0a9cf 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -140,6 +140,12 @@ static int X11_SafetyNetErrHandler(Display *d, XErrorEvent *e)
     return 0;
 }
 
+#ifdef SDL_USE_LIBDBUS
+static int X11_ShowNotification(_THIS, const SDL_NotificationData *notificationdata) {
+    return SDL_DBus_ShowNotification(notificationdata);
+}
+#endif
+
 static SDL_VideoDevice *X11_CreateDevice(void)
 {
     SDL_VideoDevice *device;
@@ -307,6 +313,10 @@ static SDL_VideoDevice *X11_CreateDevice(void)
     device->HideScreenKeyboard = X11_HideScreenKeyboard;
     device->IsScreenKeyboardShown = X11_IsScreenKeyboardShown;
 
+#ifdef SDL_USE_LIBDBUS
+    device->ShowNotification = X11_ShowNotification;
+#endif
+
     device->free = X11_DeleteDevice;
 
 #if SDL_VIDEO_VULKAN
diff --git a/test/testmessage.c b/test/testmessage.c
index f94e2a63ccb14a..6d695f97c384fe 100644
--- a/test/testmessage.c
+++ b/test/testmessage.c
@@ -200,11 +200,22 @@ int main(int argc, char *argv[])
             quit(1);
         }
 
+
+        /* Test showing a system notification message with a parent window */
+        success = SDL_ShowSimpleNotification("Simple Notification", "Hey this window needs attention!");
+        if (success == -1) {
+            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error Presenting Notification: %s\n", SDL_GetError());
+            quit(1);
+        }
+
         while (SDL_WaitEvent(&event)) {
             if (event.type == SDL_EVENT_QUIT || event.type == SDL_EVENT_KEY_UP) {
                 break;
             }
         }
+
+
+        SDL_DestroyWindow(window);
     }
 
     SDL_Quit();