diff --git a/doc/reference/libpurple/libpurple-docs.xml b/doc/reference/libpurple/libpurple-docs.xml
--- a/doc/reference/libpurple/libpurple-docs.xml
+++ b/doc/reference/libpurple/libpurple-docs.xml
@@ -75,6 +75,7 @@
       <xi:include href="xml/purpleconversationuiops.xml" />
       <xi:include href="xml/purplecredentialmanager.xml" />
       <xi:include href="xml/purplecredentialprovider.xml" />
+      <xi:include href="xml/purpledebugui.xml" />
       <xi:include href="xml/purpleimconversation.xml" />
       <xi:include href="xml/purplekeyvaluepair.xml" />
       <xi:include href="xml/purplemarkup.xml" />
diff --git a/libpurple/debug.h b/libpurple/debug.h
--- a/libpurple/debug.h
+++ b/libpurple/debug.h
@@ -42,14 +42,6 @@
 G_BEGIN_DECLS
 
 /**
- * PURPLE_TYPE_DEBUG_UI:
- *
- * The standard _get_type macro for #PurpleDebugUi.
- */
-#define PURPLE_TYPE_DEBUG_UI (purple_debug_ui_get_type())
-G_DECLARE_INTERFACE(PurpleDebugUi, purple_debug_ui, PURPLE, DEBUG_UI, GObject)
-
-/**
  * PurpleDebugLevel:
  * @PURPLE_DEBUG_ALL: All debug levels.
  * @PURPLE_DEBUG_MISC: General chatter.
@@ -70,25 +62,7 @@
 
 } PurpleDebugLevel;
 
-/**
- * PurpleDebugUiInterface:
- * @print: Called to output a debug string to the UI.
- * @is_enabled: Returns if debug printing is enabled in the UI for a @level and
- *              @category.
- *
- * Debug UI operations.
- */
-struct _PurpleDebugUiInterface {
-	/*< private >*/
-	GTypeInterface parent_iface;
-
-	/*< public >*/
-	void (*print)(PurpleDebugUi *ui, PurpleDebugLevel level, const gchar *category, const gchar *arg_s);
-	gboolean (*is_enabled)(PurpleDebugUi *ui, PurpleDebugLevel level, const gchar *category);
-
-	/*< private >*/
-	gpointer reserved[4];
-};
+#include "purpledebugui.h"
 
 /**
  * purple_debug:
@@ -269,37 +243,6 @@
  */
 PurpleDebugUi *purple_debug_get_ui(void);
 
-/**
- * purple_debug_ui_is_enabled:
- * @ui: The #PurpleDebugUi instance.
- * @level: The #PurpleLogLevel.
- * @category: An optional category.
- *
- * Checks if the ui should output messages at the given level and optional
- * category.
- *
- * Typically this function will not need to be called outside of libpurple.
- *
- * Returns: %TRUE if the given level and category will be output by @ui, %FALSE
- *          otherwise.
- *
- * Since: 3.0.0
- */
-gboolean purple_debug_ui_is_enabled(PurpleDebugUi *ui, PurpleDebugLevel level, const gchar *category);
-
-/**
- * purple_debug_ui_print:
- * @ui: The #PurpleDebugUi instance.
- * @level: The #PurpleDebugLevel.
- * @category: An optional category.
- * @arg_s: The debug string to output.
- *
- * Outputs @arg_s via @ui with the given @level and optional @category.
- *
- * Since: 3.0.0
- */
-void purple_debug_ui_print(PurpleDebugUi *ui, PurpleDebugLevel level, const gchar *category, const gchar *arg_s);
-
 /******************************************************************************
  * Debug Subsystem
  *****************************************************************************/
diff --git a/libpurple/debug.c b/libpurple/debug.c
--- a/libpurple/debug.c
+++ b/libpurple/debug.c
@@ -232,46 +232,6 @@
 	return debug_ui;
 }
 
-gboolean
-purple_debug_ui_is_enabled(PurpleDebugUi *ui, PurpleDebugLevel level,
-                           const gchar *category)
-{
-	PurpleDebugUiInterface *iface = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_DEBUG_UI(ui), FALSE);
-
-	iface = PURPLE_DEBUG_UI_GET_IFACE(ui);
-	if(iface != NULL && iface->is_enabled != NULL) {
-		return iface->is_enabled(ui, level, category);
-	}
-
-	return FALSE;
-}
-
-void
-purple_debug_ui_print(PurpleDebugUi *ui, PurpleDebugLevel level,
-                      const gchar *category, const gchar *arg_s)
-{
-	PurpleDebugUiInterface *iface = NULL;
-
-	g_return_if_fail(PURPLE_IS_DEBUG_UI(ui));
-
-	if(!purple_debug_ui_is_enabled(ui, level, category)) {
-		return;
-	}
-
-	iface = PURPLE_DEBUG_UI_GET_IFACE(ui);
-	if(iface != NULL && iface->print != NULL) {
-		iface->print(ui, level, category, arg_s);
-	}
-}
-
-G_DEFINE_INTERFACE(PurpleDebugUi, purple_debug_ui, G_TYPE_OBJECT);
-
-static void
-purple_debug_ui_default_init(PurpleDebugUiInterface *iface) {
-}
-
 void
 purple_debug_init(void) {
 	/* Read environment variables once per init */
diff --git a/libpurple/meson.build b/libpurple/meson.build
--- a/libpurple/meson.build
+++ b/libpurple/meson.build
@@ -51,6 +51,7 @@
 	'purpleconversationuiops.c',
 	'purplecredentialmanager.c',
 	'purplecredentialprovider.c',
+	'purpledebugui.c',
 	'purpleimconversation.c',
 	'purplekeyvaluepair.c',
 	'purplemarkup.c',
@@ -142,6 +143,7 @@
 	'purpleconversationuiops.h',
 	'purplecredentialmanager.h',
 	'purplecredentialprovider.h',
+	'purpledebugui.h',
 	'purpleimconversation.h',
 	'purpleattachment.h',
 	'purplekeyvaluepair.h',
diff --git a/libpurple/purpledebugui.h b/libpurple/purpledebugui.h
new file mode 100644
--- /dev/null
+++ b/libpurple/purpledebugui.h
@@ -0,0 +1,108 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
+# error "only <purple.h> may be included directly"
+#endif
+
+#ifndef PURPLE_DEBUG_UI_H
+#define PURPLE_DEBUG_UI_H
+
+/**
+ * SECTION:purpledebugui
+ * @section_id: libpurple-purpledebugui
+ * @title: Debug User Interface API
+ *
+ * #PurpleDebugUiInterface defines the behavior that libpurple uses to
+ * interface the debug API with the user interface.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PURPLE_TYPE_DEBUG_UI:
+ *
+ * The standard _get_type macro for #PurpleDebugUi.
+ */
+#define PURPLE_TYPE_DEBUG_UI (purple_debug_ui_get_type())
+G_DECLARE_INTERFACE(PurpleDebugUi, purple_debug_ui, PURPLE, DEBUG_UI, GObject)
+
+#include "debug.h"
+
+/**
+ * PurpleDebugUiInterface:
+ * @print: Called to output a debug string to the UI.
+ * @is_enabled: Returns if debug printing is enabled in the UI for a @level and
+ *              @category.
+ *
+ * Debug UI operations.
+ */
+struct _PurpleDebugUiInterface {
+	/*< private >*/
+	GTypeInterface parent_iface;
+
+	/*< public >*/
+	void (*print)(PurpleDebugUi *ui, PurpleDebugLevel level, const gchar *category, const gchar *arg_s);
+	gboolean (*is_enabled)(PurpleDebugUi *ui, PurpleDebugLevel level, const gchar *category);
+
+	/*< private >*/
+	gpointer reserved[4];
+};
+
+/**
+ * purple_debug_ui_is_enabled:
+ * @ui: The #PurpleDebugUi instance.
+ * @level: The #PurpleDebugLevel.
+ * @category: An optional category.
+ *
+ * Checks if the ui should output messages at the given level and optional
+ * category.
+ *
+ * Typically this function will not need to be called outside of libpurple.
+ *
+ * Returns: %TRUE if the given level and category will be output by @ui, %FALSE
+ *          otherwise.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_debug_ui_is_enabled(PurpleDebugUi *ui, PurpleDebugLevel level, const gchar *category);
+
+/**
+ * purple_debug_ui_print:
+ * @ui: The #PurpleDebugUi instance.
+ * @level: The #PurpleDebugLevel.
+ * @category: An optional category.
+ * @arg_s: The debug string to output.
+ *
+ * Outputs @arg_s via @ui with the given @level and optional @category.
+ *
+ * Since: 3.0.0
+ */
+void purple_debug_ui_print(PurpleDebugUi *ui, PurpleDebugLevel level, const gchar *category, const gchar *arg_s);
+
+G_END_DECLS
+
+#endif /* PURPLE_DEBUG_UI_H */
+
diff --git a/libpurple/purpledebugui.c b/libpurple/purpledebugui.c
new file mode 100644
--- /dev/null
+++ b/libpurple/purpledebugui.c
@@ -0,0 +1,64 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "purpledebugui.h"
+
+gboolean
+purple_debug_ui_is_enabled(PurpleDebugUi *ui, PurpleDebugLevel level,
+                           const gchar *category)
+{
+	PurpleDebugUiInterface *iface = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_DEBUG_UI(ui), FALSE);
+
+	iface = PURPLE_DEBUG_UI_GET_IFACE(ui);
+	if(iface != NULL && iface->is_enabled != NULL) {
+		return iface->is_enabled(ui, level, category);
+	}
+
+	return FALSE;
+}
+
+void
+purple_debug_ui_print(PurpleDebugUi *ui, PurpleDebugLevel level,
+                      const gchar *category, const gchar *arg_s)
+{
+	PurpleDebugUiInterface *iface = NULL;
+
+	g_return_if_fail(PURPLE_IS_DEBUG_UI(ui));
+
+	if(!purple_debug_ui_is_enabled(ui, level, category)) {
+		return;
+	}
+
+	iface = PURPLE_DEBUG_UI_GET_IFACE(ui);
+	if(iface != NULL && iface->print != NULL) {
+		iface->print(ui, level, category, arg_s);
+	}
+}
+
+G_DEFINE_INTERFACE(PurpleDebugUi, purple_debug_ui, G_TYPE_OBJECT);
+
+static void
+purple_debug_ui_default_init(PurpleDebugUiInterface *iface) {
+}
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -245,6 +245,7 @@
 libpurple/purpleconversation.c
 libpurple/purplecredentialmanager.c
 libpurple/purplecredentialprovider.c
+libpurple/purpledebugui.c
 libpurple/purple-gio.c
 libpurple/purpleimconversation.c
 libpurple/purplekeyvaluepair.c
