diff --git a/include/asterisk/bridge_features.h b/include/asterisk/bridge_features.h
index df01a0dca8c248673e9699f7317a25a62eeab668..7fcb85bd20be1fb07498e1b7e42fe7452074d6b1 100644
--- a/include/asterisk/bridge_features.h
+++ b/include/asterisk/bridge_features.h
@@ -53,6 +53,8 @@ enum ast_bridge_feature_flags {
 	AST_BRIDGE_FLAG_TRANSFER_PROHIBITED = (1 << 8),
 	/*! Bridge transfers require transfer of entire bridge rather than individual channels */
 	AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY = (1 << 9),
+	/*! Bridge is invisible to AMI/CLI/ARI/etc. */
+	AST_BRIDGE_FLAG_INVISIBLE = (1 << 10),
 };
 
 /*! \brief Flags used for per bridge channel features */
diff --git a/main/bridge.c b/main/bridge.c
index ce6b9601bef5480e72a84b495c32b567c531fd78..68ac24bba544b118d79bb8ac956e48473163767c 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -759,11 +759,13 @@ struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabiliti
 	ast_set_flag(&self->feature_flags, flags);
 	self->allowed_capabilities = capabilities;
 
-	if (bridge_topics_init(self) != 0) {
-		ast_log(LOG_WARNING, "Bridge %s: Could not initialize topics\n",
-			self->uniqueid);
-		ao2_ref(self, -1);
-		return NULL;
+	if (!(flags & AST_BRIDGE_FLAG_INVISIBLE)) {
+		if (bridge_topics_init(self) != 0) {
+			ast_log(LOG_WARNING, "Bridge %s: Could not initialize topics\n",
+				self->uniqueid);
+			ao2_ref(self, -1);
+			return NULL;
+		}
 	}
 
 	/* Use our helper function to find the "best" bridge technology. */
@@ -793,9 +795,11 @@ struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabiliti
 		return NULL;
 	}
 
-	if (!ast_bridge_topic(self)) {
-		ao2_ref(self, -1);
-		return NULL;
+	if (!(flags & AST_BRIDGE_FLAG_INVISIBLE)) {
+		if (!ast_bridge_topic(self)) {
+			ao2_ref(self, -1);
+			return NULL;
+		}
 	}
 
 	return self;
@@ -4321,8 +4325,8 @@ static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
 	bridge = ast_channel_get_bridge(chan);
 	ast_channel_unlock(chan);
 
-	if (bridge
-		&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) {
+	if (bridge && ast_test_flag(&bridge->feature_flags,
+			(AST_BRIDGE_FLAG_MASQUERADE_ONLY | AST_BRIDGE_FLAG_INVISIBLE))) {
 		ao2_ref(bridge, -1);
 		bridge = NULL;
 	}
diff --git a/main/manager_bridges.c b/main/manager_bridges.c
index dd3e98b8d5ec346855ea7da978fe7a3156830725..2069d507cd74fd0eace1f8f38a7ae07c6b3d6e61 100644
--- a/main/manager_bridges.c
+++ b/main/manager_bridges.c
@@ -572,7 +572,7 @@ static int manager_bridge_kick(struct mansession *s, const struct message *m)
 		}
 	} else {
 		bridge = ast_bridge_find_by_id(bridge_uniqueid);
-		if (!bridge) {
+		if (!bridge || ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_INVISIBLE)) {
 			astman_send_error(s, m, "Bridge not found");
 			return 0;
 		}
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c
index d06ee14a7a699122178caae94e6cafd0436466f6..0e46edbea3b75b2f5b689975fbe4b3403963581f 100644
--- a/main/stasis_bridges.c
+++ b/main/stasis_bridges.c
@@ -232,6 +232,10 @@ struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge
 	RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
 	struct ast_bridge_channel *bridge_channel;
 
+	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_INVISIBLE)) {
+		return NULL;
+	}
+
 	snapshot = ao2_alloc_options(sizeof(*snapshot), bridge_snapshot_dtor,
 		AO2_ALLOC_OPT_LOCK_NOLOCK);
 	if (!snapshot || ast_string_field_init(snapshot, 128)) {
@@ -371,6 +375,8 @@ void ast_bridge_publish_merge(struct ast_bridge *to, struct ast_bridge *from)
 
 	ast_assert(to != NULL);
 	ast_assert(from != NULL);
+	ast_assert(ast_test_flag(&to->feature_flags, AST_BRIDGE_FLAG_INVISIBLE) == 0);
+	ast_assert(ast_test_flag(&from->feature_flags, AST_BRIDGE_FLAG_INVISIBLE) == 0);
 
 	merge_msg = bridge_merge_message_create(to, from);
 	if (!merge_msg) {
@@ -447,6 +453,10 @@ void ast_bridge_publish_enter(struct ast_bridge *bridge, struct ast_channel *cha
 	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
 
+	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_INVISIBLE)) {
+		return;
+	}
+
 	if (swap) {
 		blob = ast_json_pack("{s: s}", "swap", ast_channel_uniqueid(swap));
 		if (!blob) {
@@ -468,6 +478,9 @@ void ast_bridge_publish_leave(struct ast_bridge *bridge, struct ast_channel *cha
 {
 	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
 
+	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_INVISIBLE)) {
+		return;
+	}
 	msg = ast_bridge_blob_create(ast_channel_left_bridge_type(), bridge, chan, NULL);
 	if (!msg) {
 		return;
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index eb1f1bc6229a92b7449593f1d7fad8500f88dfa5..e56d1b928b216fd1ad3e44f37733df4ddf1ae7de 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -256,7 +256,9 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *cha
 	ast_string_field_set(snapshot, language, ast_channel_language(chan));
 
 	if ((bridge = ast_channel_get_bridge(chan))) {
-		ast_string_field_set(snapshot, bridgeid, bridge->uniqueid);
+		if (!ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_INVISIBLE)) {
+			ast_string_field_set(snapshot, bridgeid, bridge->uniqueid);
+		}
 		ao2_cleanup(bridge);
 	}