diff --git a/libpurple/purplenotification.h b/libpurple/purplenotification.h
--- a/libpurple/purplenotification.h
+++ b/libpurple/purplenotification.h
@@ -155,7 +155,7 @@
  * Since: 3.0
  */
 PURPLE_AVAILABLE_IN_3_0
-const gchar *purple_notification_get_id(PurpleNotification *notification);
+const char *purple_notification_get_id(PurpleNotification *notification);
 
 /**
  * purple_notification_get_notification_type:
@@ -219,12 +219,12 @@
  *
  * Gets the title of @notification.
  *
- * Returns: The title of @notification.
+ * Returns: (nullable): The title of @notification.
  *
  * Since: 3.0
  */
 PURPLE_AVAILABLE_IN_3_0
-const gchar *purple_notification_get_title(PurpleNotification *notification);
+const char *purple_notification_get_title(PurpleNotification *notification);
 
 /**
  * purple_notification_set_title:
@@ -236,7 +236,32 @@
  * Since: 3.0
  */
 PURPLE_AVAILABLE_IN_3_0
-void purple_notification_set_title(PurpleNotification *notification, const gchar *title);
+void purple_notification_set_title(PurpleNotification *notification, const char *title);
+
+/**
+ * purple_notification_get_subtitle:
+ * @notification: The instance.
+ *
+ * Gets the subtitle of @notification.
+ *
+ * Returns: (nullable): The subtitle of @notification.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+const char *purple_notification_get_subtitle(PurpleNotification *notification);
+
+/**
+ * purple_notification_set_subtitle:
+ * @notification: The instance.
+ * @subtitle: (nullable): The new subtitle.
+ *
+ * Sets the subtitle of @notification.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_notification_set_subtitle(PurpleNotification *notification, const char *subtitle);
 
 /**
  * purple_notification_get_icon_name:
@@ -249,7 +274,7 @@
  * Since: 3.0
  */
 PURPLE_AVAILABLE_IN_3_0
-const gchar *purple_notification_get_icon_name(PurpleNotification *notification);
+const char *purple_notification_get_icon_name(PurpleNotification *notification);
 
 /**
  * purple_notification_set_icon_name:
@@ -261,7 +286,7 @@
  * Since: 3.0
  */
 PURPLE_AVAILABLE_IN_3_0
-void purple_notification_set_icon_name(PurpleNotification *notification, const gchar *icon_name);
+void purple_notification_set_icon_name(PurpleNotification *notification, const char *icon_name);
 
 /**
  * purple_notification_get_read:
@@ -340,7 +365,7 @@
  * Since: 3.0
  */
 PURPLE_AVAILABLE_IN_3_0
-gint purple_notification_compare(gconstpointer a, gconstpointer b);
+int purple_notification_compare(gconstpointer a, gconstpointer b);
 
 /**
  * purple_notification_delete:
# HG changeset patch
# Node ID 8205d479e134
# Parent  ca195ffd7206
diff --git a/libpurple/purplenotification.c b/libpurple/purplenotification.c
--- a/libpurple/purplenotification.c
+++ b/libpurple/purplenotification.c
@@ -27,13 +27,15 @@
 #include "purpleenums.h"
 
 typedef struct {
-	gchar *id;
+	char *id;
 	PurpleNotificationType type;
 	PurpleAccount *account;
 
 	GDateTime *created_timestamp;
-	gchar *title;
-	gchar *icon_name;
+	char *title;
+	char *subtitle;
+
+	char *icon_name;
 	gboolean read;
 	gboolean interactive;
 
@@ -56,6 +58,7 @@
 	PROP_ACCOUNT,
 	PROP_CREATED_TIMESTAMP,
 	PROP_TITLE,
+	PROP_SUBTITLE,
 	PROP_ICON_NAME,
 	PROP_READ,
 	PROP_INTERACTIVE,
@@ -65,8 +68,8 @@
 };
 static GParamSpec *properties[N_PROPERTIES] = {NULL, };
 
-G_DEFINE_FINAL_TYPE_WITH_PRIVATE(PurpleNotification, purple_notification,
-                                 G_TYPE_OBJECT)
+G_DEFINE_TYPE_WITH_PRIVATE(PurpleNotification, purple_notification,
+                           G_TYPE_OBJECT)
 
 /******************************************************************************
  * Helpers
@@ -178,6 +181,10 @@
 			g_value_set_string(value,
 			                   purple_notification_get_title(notification));
 			break;
+		case PROP_SUBTITLE:
+			g_value_set_string(value,
+			                   purple_notification_get_subtitle(notification));
+			break;
 		case PROP_ICON_NAME:
 			g_value_set_string(value,
 			                   purple_notification_get_icon_name(notification));
@@ -227,6 +234,10 @@
 			purple_notification_set_title(notification,
 			                              g_value_get_string(value));
 			break;
+		case PROP_SUBTITLE:
+			purple_notification_set_subtitle(notification,
+			                                 g_value_get_string(value));
+			break;
 		case PROP_ICON_NAME:
 			purple_notification_set_icon_name(notification,
 			                                  g_value_get_string(value));
@@ -265,6 +276,7 @@
 
 	g_clear_pointer(&priv->created_timestamp, g_date_time_unref);
 	g_clear_pointer(&priv->title, g_free);
+	g_clear_pointer(&priv->subtitle, g_free);
 	g_clear_pointer(&priv->icon_name, g_free);
 
 	if(priv->data_destroy_func != NULL) {
@@ -327,8 +339,7 @@
 		"id", "id",
 		"The identifier of the notification.",
 		NULL,
-		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS
-	);
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleNotification:type:
@@ -369,7 +380,7 @@
 		"created-timestamp", "created-timestamp",
 		"The timestamp when this notification was created.",
 		G_TYPE_DATE_TIME,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleNotification:title:
@@ -384,7 +395,22 @@
 		"title", "title",
 		"The title for the notification.",
 		NULL,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleNotification:subtitle:
+	 *
+	 * An optional subtitle for this notification. A user interface may or may
+	 * not choose to use this when displaying the notification. Regardless,
+	 * this should be a translated string.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_SUBTITLE] = g_param_spec_string(
+		"subtitle", "subtitle",
+		"The subtitle for the notification.",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleNotification:icon-name:
@@ -399,7 +425,7 @@
 		"icon-name", "icon-name",
 		"The icon name for the notification.",
 		NULL,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleNotification:read:
@@ -412,7 +438,7 @@
 		"read", "read",
 		"Whether or not the notification has been read.",
 		FALSE,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleNotification:interactive:
@@ -425,7 +451,7 @@
 		"interactive", "interactive",
 		"Whether or not the notification can be interacted with.",
 		FALSE,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * PurpleNotification:data:
@@ -476,8 +502,9 @@
 	PurpleAccount *account = NULL;
 	PurpleContactInfo *info = NULL;
 	PurpleNotification *notification = NULL;
-	gchar *title = NULL;
-	const gchar *alias = NULL, *username = NULL;
+	char *title = NULL;
+	const char *alias = NULL;
+	const char *username = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_ADD_CONTACT_REQUEST(request), NULL);
 
@@ -511,8 +538,9 @@
 	PurpleAccount *account = NULL;
 	PurpleContactInfo *info = NULL;
 	PurpleNotification *notification = NULL;
-	gchar *title = NULL;
-	const gchar *alias = NULL, *username = NULL;
+	char *title = NULL;
+	const char *alias = NULL;
+	const char *username = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_AUTHORIZATION_REQUEST(authorization_request),
 	                     NULL);
@@ -582,7 +610,7 @@
 	return notification;
 }
 
-const gchar *
+const char *
 purple_notification_get_id(PurpleNotification *notification) {
 	PurpleNotificationPrivate *priv = NULL;
 
@@ -649,7 +677,7 @@
 	                         properties[PROP_CREATED_TIMESTAMP]);
 }
 
-const gchar *
+const char *
 purple_notification_get_title(PurpleNotification *notification) {
 	PurpleNotificationPrivate *priv = NULL;
 
@@ -662,7 +690,7 @@
 
 void
 purple_notification_set_title(PurpleNotification *notification,
-                              const gchar *title)
+                              const char *title)
 {
 	PurpleNotificationPrivate *priv = NULL;
 
@@ -679,7 +707,37 @@
 	}
 }
 
-const gchar *
+const char *
+purple_notification_get_subtitle(PurpleNotification *notification) {
+	PurpleNotificationPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_NOTIFICATION(notification), NULL);
+
+	priv = purple_notification_get_instance_private(notification);
+
+	return priv->subtitle;
+}
+
+void
+purple_notification_set_subtitle(PurpleNotification *notification,
+                                 const char *subtitle)
+{
+	PurpleNotificationPrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_NOTIFICATION(notification));
+
+	priv = purple_notification_get_instance_private(notification);
+
+	if(!purple_strequal(priv->subtitle, subtitle)) {
+		g_free(priv->subtitle);
+		priv->subtitle = g_strdup(subtitle);
+
+		g_object_notify_by_pspec(G_OBJECT(notification),
+		                         properties[PROP_SUBTITLE]);
+	}
+}
+
+const char *
 purple_notification_get_icon_name(PurpleNotification *notification) {
 	PurpleNotificationPrivate *priv = NULL;
 
@@ -692,7 +750,7 @@
 
 void
 purple_notification_set_icon_name(PurpleNotification *notification,
-                                  const gchar *icon_name)
+                                  const char *icon_name)
 {
 	PurpleNotificationPrivate *priv = NULL;
 
@@ -777,7 +835,7 @@
 	return priv->data;
 }
 
-gint
+int
 purple_notification_compare(gconstpointer a, gconstpointer b) {
 	PurpleNotification *notification_a = NULL;
 	PurpleNotification *notification_b = NULL;
diff --git a/libpurple/tests/test_notification.c b/libpurple/tests/test_notification.c
--- a/libpurple/tests/test_notification.c
+++ b/libpurple/tests/test_notification.c
@@ -101,54 +101,67 @@
 static void
 test_purple_notification_properties(void) {
 	PurpleNotification *notification = NULL;
-	GDateTime *ts1 = NULL, *ts2 = NULL;
+	GDateTime *created_timestamp = NULL;
+	GDateTime *created_timestamp1 = NULL;
+	char *icon_name1 = NULL;
+	char *subtitle1 = NULL;
+	char *title1 = NULL;
+	gboolean interactive = FALSE;
+	gboolean read = FALSE;
+
+	created_timestamp = g_date_time_new_now_utc();
 
-	notification = purple_notification_new(PURPLE_NOTIFICATION_TYPE_GENERIC,
-	                                       NULL,
-	                                       NULL,
-	                                       NULL);
+	notification = g_object_new(
+		PURPLE_TYPE_NOTIFICATION,
+		"created-timestamp", created_timestamp,
+		"icon-name", "icon-name",
+		"interactive", TRUE,
+		"read", TRUE,
+		"subtitle", "subtitle",
+		"title", "title",
+		"type", PURPLE_NOTIFICATION_TYPE_GENERIC,
+		NULL);
 
 	g_assert_true(PURPLE_IS_NOTIFICATION(notification));
 
-	/* Set the timestamp to current utc and verify it was set properly. */
-	ts1 = g_date_time_new_now_utc();
-	purple_notification_set_created_timestamp(notification, ts1);
-	ts2 = purple_notification_get_created_timestamp(notification);
-	g_assert_true(g_date_time_equal(ts1, ts2));
-	g_date_time_unref(ts1);
+	g_object_get(
+		notification,
+		"created-timestamp", &created_timestamp1,
+		"icon-name", &icon_name1,
+		"interactive", &interactive,
+		"read", &read,
+		"subtitle", &subtitle1,
+		"title", &title1,
+		NULL);
 
-	/* Set the title and verify it was set properly. */
-	purple_notification_set_title(notification, "title");
-	g_assert_true(purple_strequal(purple_notification_get_title(notification),
-	                              "title"));
+	g_assert_nonnull(created_timestamp1);
+	g_assert_true(g_date_time_equal(created_timestamp, created_timestamp1));
+	g_date_time_unref(created_timestamp1);
 
-	/* Set the title and verify it was set properly. */
-	purple_notification_set_icon_name(notification, "icon-name");
-	g_assert_true(purple_strequal(purple_notification_get_icon_name(notification),
-	                              "icon-name"));
+	g_assert_cmpstr(title1, ==, "title");
+	g_clear_pointer(&title1, g_free);
+
+	g_assert_cmpstr(subtitle1, ==, "subtitle");
+	g_clear_pointer(&subtitle1, g_free);
 
-	/* Set the read state and verify it. */
-	purple_notification_set_read(notification, TRUE);
-	g_assert_true(purple_notification_get_read(notification));
-	purple_notification_set_read(notification, FALSE);
-	g_assert_false(purple_notification_get_read(notification));
+	g_assert_cmpstr(icon_name1, ==, "icon-name");
+	g_clear_pointer(&icon_name1, g_free);
+
+	g_assert_true(read);
 
-	/* Set the interactive state and verify it. */
-	purple_notification_set_interactive(notification, TRUE);
-	g_assert_true(purple_notification_get_interactive(notification));
-	purple_notification_set_interactive(notification, FALSE);
-	g_assert_false(purple_notification_get_interactive(notification));
+	g_assert_true(interactive);
 
-	/* Cleanup. */
-	g_clear_object(&notification);
+	g_assert_finalize_object(notification);
+
+	g_date_time_unref(created_timestamp);
 }
 
 /******************************************************************************
  * Main
  *****************************************************************************/
-gint
-main(gint argc, gchar *argv[]) {
-	gint ret = 0;
+int
+main(int argc, char *argv[]) {
+	int ret = 0;
 
 	g_test_init(&argc, &argv, NULL);
 
