diff --git a/modules/meson.build b/modules/meson.build
index 5db780e60317809add3259babc9c6271779ba3ed..adefe23ba33fd166fe6825e940882f413b8a2db6 100644
--- a/modules/meson.build
+++ b/modules/meson.build
@@ -25,7 +25,7 @@ shared_library(
   c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pipewire"'],
   install : true,
   install_dir : wireplumber_module_dir,
-  dependencies : [wp_dep, pipewire_dep],
+  dependencies : [gio_dep, wp_dep, pipewire_dep],
 )
 
 shared_library(
@@ -47,7 +47,7 @@ shared_library(
   c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pw-audio-softdsp-endpoint"'],
   install : true,
   install_dir : wireplumber_module_dir,
-  dependencies : [wp_dep, pipewire_dep],
+  dependencies : [gio_dep, wp_dep, pipewire_dep],
 )
 
 shared_library(
diff --git a/modules/module-pipewire.c b/modules/module-pipewire.c
index a329fb8b9d13d8fa4107e34d6a80e08bafc51cf8..831e4f966bebd6354569b94f883e6f31eeca2bc7 100644
--- a/modules/module-pipewire.c
+++ b/modules/module-pipewire.c
@@ -14,12 +14,11 @@
 
 #include <wp/wp.h>
 #include <pipewire/pipewire.h>
-#include <spa/param/audio/format-utils.h>
 
 void remote_endpoint_init (WpCore * core, struct pw_core * pw_core,
     struct pw_remote * remote);
-gpointer simple_endpoint_factory (WpFactory * factory, GType type,
-    GVariant * properties);
+void simple_endpoint_factory (WpFactory * factory, GType type,
+    GVariant * properties, GAsyncReadyCallback ready, gpointer user_data);
 gpointer simple_endpoint_link_factory (WpFactory * factory, GType type,
     GVariant * properties);
 
@@ -27,158 +26,40 @@ struct module_data
 {
   WpModule *module;
   WpRemotePipewire *remote_pipewire;
-  GHashTable *client_nodes_info;
-};
-
-struct endpoint_info
-{
-  gchar *name;
-  gchar *media_class;
-  struct pw_proxy *proxy;
-};
-
-struct proxy_info
-{
-  const struct module_data *data;
-  uint32_t node_id;
-  WpProxyPort *proxy_port;
 };
 
 static void
-endpoint_info_destroy(gpointer p)
+on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d)
 {
-  struct endpoint_info *ei = p;
-
-  /* Free the name */
-  g_free (ei->name);
-
-  /* Free the media class */
-  g_free (ei->media_class);
-
-  /* Clean up */
-  g_slice_free (struct endpoint_info, p);
-}
-
-static void
-proxy_info_destroy(gpointer p)
-{
-  struct proxy_info *pi = p;
-
-  /* Unref the proxy port */
-  g_clear_object (&pi->proxy_port);
-
-  /* Clean up */
-  g_slice_free (struct proxy_info, p);
-}
-
-static void
-unregister_endpoint (WpProxy* wp_proxy, WpEndpoint *endpoint)
-{
-  g_return_if_fail(WP_IS_PROXY(wp_proxy));
-  g_return_if_fail(WP_IS_ENDPOINT(endpoint));
-
-  /* Unregister the endpoint */
-  wp_endpoint_unregister(endpoint);
-}
-
-static void
-proxy_node_created(GObject *initable, GAsyncResult *res, gpointer d)
-{
-  struct proxy_info *pi = d;
-  const struct module_data *data = pi->data;
-  g_autoptr (WpCore) core = wp_module_get_core (data->module);
-  g_autoptr (WpProxyNode) proxy_node = NULL;
-  struct endpoint_info *ei = NULL;
   g_autoptr (WpEndpoint) endpoint = NULL;
-  g_autoptr (GVariant) endpoint_props = NULL;
-  GVariantBuilder b;
-
-  /* Get the proxy */
-  proxy_node = wp_proxy_node_new_finish(initable, res, NULL);
-  if (!proxy_node)
-    return;
+  guint global_id = 0;
 
-  /* Get the client node info */
-  ei = g_hash_table_lookup(data->client_nodes_info,
-      GINT_TO_POINTER(pi->node_id));
-  if (!ei)
+  /* Get the endpoint */
+  endpoint = wp_endpoint_new_finish(initable, res, NULL);
+  if (!endpoint)
     return;
 
-  /* Set the properties */
-  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
-  g_variant_builder_add (&b, "{sv}",
-      "name", ei->name ? g_variant_new_string (ei->name) :
-          g_variant_new_take_string (
-              g_strdup_printf ("Stream %u", pi->node_id)));
-  g_variant_builder_add (&b, "{sv}",
-      "media-class", g_variant_new_string (ei->media_class));
-  g_variant_builder_add (&b, "{sv}",
-      "proxy-node", g_variant_new_uint64 ((guint64) proxy_node));
-  g_variant_builder_add (&b, "{sv}",
-      "proxy-port", g_variant_new_uint64 ((guint64) pi->proxy_port));
-  endpoint_props = g_variant_builder_end (&b);
-
-  /* Create the endpoint */
-  endpoint = wp_factory_make (core, "pipewire-simple-endpoint",
-      WP_TYPE_ENDPOINT, endpoint_props);
+  /* Get the endpoint global id */
+  g_object_get (endpoint, "global-id", &global_id, NULL);
+  g_debug ("Created client endpoint for global id %d", global_id);
 
   /* Register the endpoint */
   wp_endpoint_register (endpoint);
-
-  /* Set destroy handler to unregister endpoint when the proxy is detroyed */
-  g_signal_connect (proxy_node, "destroyed", G_CALLBACK(unregister_endpoint),
-      endpoint);
-
-  /* Clean up */
-  proxy_info_destroy (pi);
 }
 
 static void
-proxy_port_created(GObject *initable, GAsyncResult *res, gpointer d)
-{
-  struct proxy_info *pi = d;
-  const struct module_data *data = pi->data;
-  struct endpoint_info *ei = NULL;
-  WpProxyPort *proxy_port = NULL;
-
-  /* Get the proxy port */
-  proxy_port = wp_proxy_port_new_finish(initable, res, NULL);
-  if (!proxy_port)
-    return;
-
-  /* Forward the proxy port */
-  pi->proxy_port = proxy_port;
-
-  /* Get the node proxy */
-  ei = g_hash_table_lookup(data->client_nodes_info,
-      GINT_TO_POINTER(pi->node_id));
-  if (!ei)
-    return;
-
-  /* Create the proxy node asynchronically */
-  wp_proxy_node_new(pi->node_id, ei->proxy, proxy_node_created, pi);
-}
-
-static void
-handle_node (WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
+on_node_added (WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
     gpointer d)
 {
   struct module_data *data = d;
   const struct spa_dict *props = p;
-  struct endpoint_info *ei = NULL;
-  const gchar *name;
-  const gchar *media_class;
-  struct pw_proxy *proxy;
-  struct spa_audio_info_raw format = { 0, };
-  struct spa_pod *param;
-  struct spa_pod_builder pod_builder = { 0, };
-  char buf[1024];
+  g_autoptr (WpCore) core = wp_module_get_core (data->module);
+  const gchar *name, *media_class;
+  GVariantBuilder b;
+  g_autoptr (GVariant) endpoint_props = NULL;
 
   /* Make sure the node has properties */
-  if (!props) {
-    g_warning("node has no properties, skipping...");
-    return;
-  }
+  g_return_if_fail(props);
 
   /* Get the media_class */
   media_class = spa_dict_lookup(props, "media.class");
@@ -192,67 +73,20 @@ handle_node (WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
   if (!name)
     name = spa_dict_lookup (props, "node.name");
 
-  g_debug ("found stream node: id:%u ; name:%s ; media_class:%s", id, name,
-      media_class);
-
-  /* Bind the proxy */
-  proxy = wp_remote_pipewire_proxy_bind(rp, id, PW_TYPE_INTERFACE_Node);
-  if (!proxy)
-    return;
-
-  /* TODO: Assume all clients have this format for now */
-  format.format = SPA_AUDIO_FORMAT_F32P;
-  format.flags = 1;
-  format.rate = 48000;
-  format.channels = 1;
-  format.position[0] = 0;
-
-  /* Set the profile */
-  spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
-  param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
-  param = spa_pod_builder_add_object(&pod_builder,
-      SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
-      SPA_PARAM_PROFILE_direction,  SPA_POD_Id(PW_DIRECTION_OUTPUT),
-      SPA_PARAM_PROFILE_format,     SPA_POD_Pod(param));
-  pw_node_proxy_set_param((struct pw_node_proxy*)proxy,
-      SPA_PARAM_Profile, 0, param);
-
-  /* Create the endpoint info */
-  ei = g_slice_new0 (struct endpoint_info);
-  ei->name = g_strdup(name);
-  ei->media_class = g_strdup(media_class);
-  ei->proxy = proxy;
-
-  /* Insert the client node info in the hash table */
-  g_hash_table_insert(data->client_nodes_info, GINT_TO_POINTER (id), ei);
-}
-
-static void
-handle_port(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
-    gpointer d)
-{
-  struct module_data *data = d;
-  struct proxy_info *pi = NULL;
-  struct pw_proxy *proxy = NULL;
-
-  /* Only handle ports whose parent is an alsa node */
-  if (!g_hash_table_contains(data->client_nodes_info,
-      GINT_TO_POINTER (parent_id)))
-    return;
-
-  /* Bind the proxy */
-  proxy = wp_remote_pipewire_proxy_bind (rp, id, PW_TYPE_INTERFACE_Port);
-  if (!proxy)
-    return;
-
-  /* Create the port info */
-  pi = g_slice_new0 (struct proxy_info);
-  pi->data = data;
-  pi->node_id = parent_id;
-  pi->proxy_port = NULL;
+  /* Set the properties */
+  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&b, "{sv}",
+      "name", name ? g_variant_new_string (name) :
+          g_variant_new_take_string (g_strdup_printf ("Stream %u", id)));
+  g_variant_builder_add (&b, "{sv}",
+      "media-class", g_variant_new_string (media_class));
+  g_variant_builder_add (&b, "{sv}",
+      "global-id", g_variant_new_uint32 (id));
+  endpoint_props = g_variant_builder_end (&b);
 
-  /* Create the proxy port asynchronically */
-  wp_proxy_port_new(id, proxy, proxy_port_created, pi);
+  /* Create the endpoint async */
+  wp_factory_make_async (core, "pipewire-simple-endpoint", WP_TYPE_ENDPOINT,
+      endpoint_props, on_endpoint_created, data);
 }
 
 static void
@@ -264,10 +98,6 @@ module_destroy (gpointer d)
   data->module = NULL;
   data->remote_pipewire = NULL;
 
-  /* Destroy the hash table */
-  g_hash_table_unref (data->client_nodes_info);
-  data->client_nodes_info = NULL;
-
   /* Clean up */
   g_slice_free (struct module_data, data);
 }
@@ -292,22 +122,20 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
   data = g_slice_new0 (struct module_data);
   data->module = module;
   data->remote_pipewire = rp;
-  data->client_nodes_info = g_hash_table_new_full (g_direct_hash,
-      g_direct_equal, NULL, endpoint_info_destroy);
 
   /* Set the module destroy callback */
   wp_module_set_destroy_callback (module, module_destroy, data);
 
-  /* Register the global addded callbacks */
-  g_signal_connect(rp, "global-added::node", (GCallback)handle_node, data);
-  g_signal_connect(rp, "global-added::port", (GCallback)handle_port, data);
+  /* Register the global added/removed callbacks */
+  g_signal_connect(rp, "global-added::node", (GCallback)on_node_added, data);
 
   /* Init remoted endpoint */
   g_object_get (rp, "pw-core", &pw_core, "pw-remote", &pw_remote, NULL);
   remote_endpoint_init (core, pw_core, pw_remote);
 
   /* Register simple-endpoint and simple-endpoint-link */
-  wp_factory_new (core, "pipewire-simple-endpoint", simple_endpoint_factory);
+  wp_factory_new_async (core, "pipewire-simple-endpoint",
+      simple_endpoint_factory);
   wp_factory_new (core, "pipewire-simple-endpoint-link",
       simple_endpoint_link_factory);
 }
diff --git a/modules/module-pipewire/simple-endpoint.c b/modules/module-pipewire/simple-endpoint.c
index 6f9aa6937ccf98941396b3a048f926b0b55827e2..d3d33766c2a6df2f162b08bffa02bfd6f730b477 100644
--- a/modules/module-pipewire/simple-endpoint.c
+++ b/modules/module-pipewire/simple-endpoint.c
@@ -13,6 +13,8 @@
  * other arbitrary node that does not need any kind of internal management.
  */
 
+#include <spa/param/audio/format-utils.h>
+
 #include <wp/wp.h>
 #include <pipewire/pipewire.h>
 #include <spa/pod/parser.h>
@@ -22,10 +24,19 @@ struct _WpPipewireSimpleEndpoint
 {
   WpEndpoint parent;
 
-  /* Proxy */
+  /* The global-id this endpoint refers to */
+  guint global_id;
+
+  /* The task to signal the endpoint is initialized */
+  GTask *init_task;
+
+  /* The remote pipewire */
+  WpRemotePipewire *remote_pipewire;
+
+  /* Proxies */
   WpProxyNode *proxy_node;
-  WpProxyPort *proxy_port;
   struct spa_hook node_proxy_listener;
+  GPtrArray *proxies_port;
 
   /* controls cache */
   gfloat volume;
@@ -34,8 +45,7 @@ struct _WpPipewireSimpleEndpoint
 
 enum {
   PROP_0,
-  PROP_NODE_PROXY,
-  PROP_PORT_PROXY
+  PROP_GLOBAL_ID,
 };
 
 enum {
@@ -43,10 +53,17 @@ enum {
   CONTROL_MUTE,
 };
 
+static GAsyncInitableIface *wp_simple_endpoint_parent_interface = NULL;
+static void wp_simple_endpoint_async_initable_init (gpointer iface,
+    gpointer iface_data);
+
 G_DECLARE_FINAL_TYPE (WpPipewireSimpleEndpoint,
     simple_endpoint, WP_PIPEWIRE, SIMPLE_ENDPOINT, WpEndpoint)
 
-G_DEFINE_TYPE (WpPipewireSimpleEndpoint, simple_endpoint, WP_TYPE_ENDPOINT)
+G_DEFINE_TYPE_WITH_CODE (WpPipewireSimpleEndpoint, simple_endpoint,
+    WP_TYPE_ENDPOINT,
+    G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                           wp_simple_endpoint_async_initable_init))
 
 static void
 node_proxy_param (void *object, int seq, uint32_t id,
@@ -100,21 +117,122 @@ static const struct pw_node_proxy_events node_node_proxy_events = {
 };
 
 static void
-simple_endpoint_init (WpPipewireSimpleEndpoint * self)
+on_proxy_node_destroyed (WpProxy* wp_proxy, WpEndpoint *endpoint)
 {
+  g_return_if_fail(WP_IS_ENDPOINT(endpoint));
+
+  /* Unregister the endpoint */
+  wp_endpoint_unregister(endpoint);
 }
 
 static void
-simple_endpoint_constructed (GObject * object)
+on_all_ports_done(WpProxy *proxy, gpointer data)
 {
-  WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (object);
+  WpPipewireSimpleEndpoint *self = data;
+
+  /* Don't do anything if the endpoint has already been initialized */
+  if (!self->init_task)
+    return;
+
+  /* Set destroy handler to unregister endpoint on proxy_node destruction */
+  g_signal_connect (self->proxy_node, "destroyed",
+      G_CALLBACK(on_proxy_node_destroyed), WP_ENDPOINT(self));
+
+  /* Finish the creation of the endpoint */
+  g_task_return_boolean (self->init_task, TRUE);
+  g_clear_object(&self->init_task);
+}
+
+static void
+on_proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data)
+{
+  WpPipewireSimpleEndpoint *self = data;
+  WpProxyPort *proxy_port = NULL;
+
+  /* Get the proxy port */
+  proxy_port = wp_proxy_port_new_finish(initable, res, NULL);
+  g_return_if_fail (proxy_port);
+
+  /* Add the proxy port to the array */
+  g_return_if_fail (self->proxies_port);
+  g_ptr_array_add(self->proxies_port, proxy_port);
+
+  /* Register the done callback */
+  g_signal_connect(self->proxy_node, "done", (GCallback)on_all_ports_done,
+    self);
+  wp_proxy_sync (WP_PROXY(self->proxy_node));
+}
+
+static void
+on_port_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
+    gpointer d)
+{
+  WpPipewireSimpleEndpoint *self = d;
+  struct pw_port_proxy *port_proxy = NULL;
+
+  /* Only handle ports owned by this endpoint */
+  if (parent_id != self->global_id)
+    return;
+
+  /* Create the proxy port async */
+  port_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire, id,
+    PW_TYPE_INTERFACE_Port);
+  g_return_if_fail(port_proxy);
+  wp_proxy_port_new(id, port_proxy, on_proxy_port_created, self);
+}
+
+static void
+emit_endpoint_ports(WpPipewireSimpleEndpoint *self)
+{
+  struct pw_node_proxy* node_proxy = NULL;
+  struct spa_audio_info_raw format = { 0, };
+  struct spa_pod *param;
+  struct spa_pod_builder pod_builder = { 0, };
+  char buf[1024];
+
+  /* Get the pipewire node proxy */
+  node_proxy = wp_proxy_get_pw_proxy(WP_PROXY(self->proxy_node));
+  g_return_if_fail (node_proxy);
+
+  /* TODO: Assume all clients have this format for now */
+  format.format = SPA_AUDIO_FORMAT_F32P;
+  format.flags = 1;
+  format.rate = 48000;
+  format.channels = 2;
+  format.position[0] = 0;
+  format.position[1] = 0;
+
+  /* Build the param profile */
+  spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
+  param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
+  param = spa_pod_builder_add_object(&pod_builder,
+      SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
+      SPA_PARAM_PROFILE_direction,  SPA_POD_Id(PW_DIRECTION_OUTPUT),
+      SPA_PARAM_PROFILE_format,     SPA_POD_Pod(param));
+
+  /* Set the param profile to emit the ports */
+  pw_node_proxy_set_param(node_proxy, SPA_PARAM_Profile, 0, param);
+}
+
+static void
+on_proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data)
+{
+  WpPipewireSimpleEndpoint *self = data;
   GVariantDict d;
   uint32_t ids[1] = { SPA_PARAM_Props };
   uint32_t n_ids = 1;
   struct pw_node_proxy *node_proxy = NULL;
 
+  /* Get the proxy node */
+  self->proxy_node = wp_proxy_node_new_finish(initable, res, NULL);
+  g_return_if_fail (self->proxy_node);
+
+  /* Emit the ports */
+  emit_endpoint_ports(self);
+
   /* Add a custom node proxy event listener */
   node_proxy = wp_proxy_get_pw_proxy(WP_PROXY(self->proxy_node));
+  g_return_if_fail (node_proxy);
   pw_node_proxy_add_listener (node_proxy, &self->node_proxy_listener,
       &node_node_proxy_events, self);
   pw_node_proxy_subscribe_params (node_proxy, ids, n_ids);
@@ -143,8 +261,54 @@ simple_endpoint_constructed (GObject * object)
     g_variant_dict_insert (&d, "default-value", "b", FALSE);
     wp_endpoint_register_control (WP_ENDPOINT (self), g_variant_dict_end (&d));
   }
+}
+
+static void
+wp_simple_endpoint_init_async (GAsyncInitable *initable, int io_priority,
+    GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
+{
+  WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (initable);
+  g_autoptr (WpCore) core = wp_endpoint_get_core(WP_ENDPOINT(self));
+  struct pw_node_proxy *node_proxy = NULL;
+
+  /* Create the async task */
+  self->init_task = g_task_new (initable, cancellable, callback, data);
+
+  /* Init the proxies_port array */
+  self->proxies_port = g_ptr_array_new_full(2, (GDestroyNotify)g_object_unref);
+
+  /* Register a port_added callback */
+  self->remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE);
+  g_return_if_fail(self->remote_pipewire);
+  g_signal_connect(self->remote_pipewire, "global-added::port",
+      (GCallback)on_port_added, self);
+
+  /* Create the proxy node async */
+  node_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire,
+      self->global_id, PW_TYPE_INTERFACE_Node);
+  g_return_if_fail(node_proxy);
+  wp_proxy_node_new(self->global_id, node_proxy, on_proxy_node_created, self);
+
+  /* Call the parent interface */
+  wp_simple_endpoint_parent_interface->init_async (initable, io_priority,
+      cancellable, callback, data);
+}
+
+static void
+wp_simple_endpoint_async_initable_init (gpointer iface, gpointer iface_data)
+{
+  GAsyncInitableIface *ai_iface = iface;
+
+  /* Set the parent interface */
+  wp_simple_endpoint_parent_interface = g_type_interface_peek_parent (iface);
+
+  /* Only set the init_async */
+  ai_iface->init_async = wp_simple_endpoint_init_async;
+}
 
-  G_OBJECT_CLASS (simple_endpoint_parent_class)->constructed (object);
+static void
+simple_endpoint_init (WpPipewireSimpleEndpoint * self)
+{
 }
 
 static void
@@ -152,12 +316,18 @@ simple_endpoint_finalize (GObject * object)
 {
   WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (object);
 
-  /* Unref the proxy node */
-  if (self->proxy_node) {
-    g_object_unref(self->proxy_node);
-    self->proxy_node = NULL;
+  /* Destroy the proxies port */
+  if (self->proxies_port) {
+    g_ptr_array_free(self->proxies_port, TRUE);
+    self->proxies_port = NULL;
   }
 
+  /* Destroy the proxy node */
+  g_clear_object(&self->proxy_node);
+
+  /* Destroy the done task */
+  g_clear_object(&self->init_task);
+
   G_OBJECT_CLASS (simple_endpoint_parent_class)->finalize (object);
 }
 
@@ -168,13 +338,8 @@ simple_endpoint_set_property (GObject * object, guint property_id,
   WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (object);
 
   switch (property_id) {
-  case PROP_NODE_PROXY:
-    g_clear_object(&self->proxy_node);
-    self->proxy_node = g_value_dup_object(value);
-    break;
-  case PROP_PORT_PROXY:
-    g_clear_object(&self->proxy_port);
-    self->proxy_port = g_value_dup_object(value);
+  case PROP_GLOBAL_ID:
+    self->global_id = g_value_get_uint(value);
     break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -189,11 +354,8 @@ simple_endpoint_get_property (GObject * object, guint property_id,
   WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (object);
 
   switch (property_id) {
-  case PROP_NODE_PROXY:
-    g_value_set_object (value, self->proxy_node);
-    break;
-  case PROP_PORT_PROXY:
-    g_value_set_object (value, self->proxy_port);
+  case PROP_GLOBAL_ID:
+    g_value_set_uint (value, self->global_id);
     break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -206,16 +368,14 @@ simple_endpoint_prepare_link (WpEndpoint * ep, guint32 stream_id,
     WpEndpointLink * link, GVariant ** properties, GError ** error)
 {
   WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (ep);
-  uint32_t node_id = wp_proxy_get_global_id(WP_PROXY(self->proxy_node));
-  uint32_t port_id = wp_proxy_get_global_id(WP_PROXY(self->proxy_port));
   GVariantBuilder b;
 
   /* Set the properties */
   g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
   g_variant_builder_add (&b, "{sv}", "node-id",
-      g_variant_new_uint32 (node_id));
+      g_variant_new_uint32 (self->global_id));
   g_variant_builder_add (&b, "{sv}", "node-port-id",
-      g_variant_new_uint32 (port_id));
+      g_variant_new_uint32 (-1));
   *properties = g_variant_builder_end (&b);
 
   return TRUE;
@@ -294,7 +454,6 @@ simple_endpoint_class_init (WpPipewireSimpleEndpointClass * klass)
   GObjectClass *object_class = (GObjectClass *) klass;
   WpEndpointClass *endpoint_class = (WpEndpointClass *) klass;
 
-  object_class->constructed = simple_endpoint_constructed;
   object_class->finalize = simple_endpoint_finalize;
   object_class->set_property = simple_endpoint_set_property;
   object_class->get_property = simple_endpoint_get_property;
@@ -303,48 +462,40 @@ simple_endpoint_class_init (WpPipewireSimpleEndpointClass * klass)
   endpoint_class->get_control_value = simple_endpoint_get_control_value;
   endpoint_class->set_control_value = simple_endpoint_set_control_value;
 
-  g_object_class_install_property (object_class, PROP_NODE_PROXY,
-      g_param_spec_object ("node-proxy", "node-proxy",
-          "Pointer to the node proxy of the client", WP_TYPE_PROXY_NODE,
-          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-  g_object_class_install_property (object_class, PROP_PORT_PROXY,
-      g_param_spec_object ("port-proxy", "port-proxy",
-          "Pointer to the port proxy of the client", WP_TYPE_PROXY_PORT,
+  g_object_class_install_property (object_class, PROP_GLOBAL_ID,
+      g_param_spec_uint ("global-id", "global-id",
+          "The global Id this endpoint refers to", 0, G_MAXUINT, 0,
           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 }
 
-gpointer
+void
 simple_endpoint_factory (WpFactory * factory, GType type,
-    GVariant * properties)
+    GVariant * properties, GAsyncReadyCallback ready, gpointer user_data)
 {
   g_autoptr (WpCore) core = NULL;
-  guint64 proxy_node;
-  guint64 proxy_port;
-  const gchar *name;
-  const gchar *media_class;
+  const gchar *name, *media_class;
+  guint global_id;
 
-  g_return_val_if_fail (type == WP_TYPE_ENDPOINT, NULL);
-  g_return_val_if_fail (properties != NULL, NULL);
-  g_return_val_if_fail (g_variant_is_of_type (properties,
-          G_VARIANT_TYPE_VARDICT), NULL);
+  /* Make sure the type is correct */
+  g_return_if_fail (type == WP_TYPE_ENDPOINT);
 
+  /* Get the Core */
   core = wp_factory_get_core (factory);
-  g_return_val_if_fail (core != NULL, NULL);
+  g_return_if_fail (core);
 
+  /* Get the properties */
   if (!g_variant_lookup (properties, "name", "&s", &name))
-      return NULL;
+      return;
   if (!g_variant_lookup (properties, "media-class", "&s", &media_class))
-      return NULL;
-  if (!g_variant_lookup (properties, "proxy-node", "t", &proxy_node))
-      return NULL;
-  if (!g_variant_lookup (properties, "proxy-port", "t", &proxy_port))
-      return NULL;
+      return;
+  if (!g_variant_lookup (properties, "global-id", "u", &global_id))
+      return;
 
-  return g_object_new (simple_endpoint_get_type (),
+  g_async_initable_new_async (
+      simple_endpoint_get_type (), G_PRIORITY_DEFAULT, NULL, ready, user_data,
       "core", core,
       "name", name,
       "media-class", media_class,
-      "node-proxy", (gpointer) proxy_node,
-      "port-proxy", (gpointer) proxy_port,
+      "global-id", global_id,
       NULL);
 }
diff --git a/modules/module-pw-alsa-udev.c b/modules/module-pw-alsa-udev.c
index c98cd76cf713d263b30236bdd813e1bbb8cc141a..576e1e2cd141b38a86dad3d99c14f520cc9b5fe5 100644
--- a/modules/module-pw-alsa-udev.c
+++ b/modules/module-pw-alsa-udev.c
@@ -19,143 +19,37 @@ struct impl
 {
   WpModule *module;
   WpRemotePipewire *remote_pipewire;
-  GHashTable *alsa_nodes_info;
 };
 
-struct endpoint_info
-{
-  gchar *name;
-  gchar *media_class;
-};
-
-struct proxy_info
-{
-  const struct impl *impl;
-  uint32_t node_id;
-  WpProxyPort *proxy_port;
-};
-
-static void
-endpoint_info_destroy(gpointer p)
-{
-  struct endpoint_info *ei = p;
-
-  /* Free the name */
-  g_free (ei->name);
-
-  /* Free the media class */
-  g_free (ei->media_class);
-
-  /* Clean up */
-  g_slice_free (struct endpoint_info, p);
-}
-
 static void
-proxy_info_destroy(gpointer p)
+on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d)
 {
-  struct proxy_info *pi = p;
-
-  /* Unref the proxy port */
-  g_clear_object (&pi->proxy_port);
-
-  /* Clean up */
-  g_slice_free (struct proxy_info, p);
-}
-
-static void
-unregister_endpoint (WpProxy* wp_proxy, WpEndpoint *endpoint)
-{
-  g_return_if_fail(WP_IS_PROXY(wp_proxy));
-  g_return_if_fail(WP_IS_ENDPOINT(endpoint));
-
-  /* Unregister the endpoint */
-  wp_endpoint_unregister(endpoint);
-}
-
-static void
-proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data)
-{
-  struct proxy_info *pi = data;
-  const struct impl *impl = pi->impl;
-  g_autoptr (WpCore) core = wp_module_get_core (impl->module);
-  g_autoptr(WpProxyNode) proxy_node = NULL;
-  struct endpoint_info *ei = NULL;
-  GVariantBuilder b;
-  g_autoptr (GVariant) endpoint_props = NULL;
   g_autoptr (WpEndpoint) endpoint = NULL;
+  guint global_id = 0;
 
-  /* Get the proxy */
-  proxy_node = wp_proxy_node_new_finish(initable, res, NULL);
-  if (!proxy_node)
-    return;
-
-  /* Get the alsa node info */
-  ei = g_hash_table_lookup(impl->alsa_nodes_info, GINT_TO_POINTER(pi->node_id));
-  if (!ei)
+  /* Get the endpoint */
+  endpoint = wp_endpoint_new_finish(initable, res, NULL);
+  if (!endpoint)
     return;
 
-  /* Build the properties for the endpoint */
-  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
-  g_variant_builder_add (&b, "{sv}", "name",
-      g_variant_new_take_string (g_strdup_printf ("Endpoint %u: %s",
-          pi->node_id, ei->name)));
-  g_variant_builder_add (&b, "{sv}",
-      "media-class", g_variant_new_string (ei->media_class));
-  g_variant_builder_add (&b, "{sv}",
-      "proxy-node", g_variant_new_uint64 ((guint64) proxy_node));
-  g_variant_builder_add (&b, "{sv}",
-      "proxy-port", g_variant_new_uint64 ((guint64) pi->proxy_port));
-  endpoint_props = g_variant_builder_end (&b);
-
-  /* Create and register the endpoint */
-  endpoint = wp_factory_make (core, "pw-audio-softdsp-endpoint",
-      WP_TYPE_ENDPOINT, endpoint_props);
+  /* Get the endpoint global id */
+  g_object_get (endpoint, "global-id", &global_id, NULL);
+  g_debug ("Created alsa endpoint for global id %d", global_id);
 
   /* Register the endpoint */
   wp_endpoint_register (endpoint);
-
-  /* Set destroy handler to unregister endpoint when the proxy is detroyed */
-  g_signal_connect (proxy_node, "destroyed", G_CALLBACK(unregister_endpoint),
-      endpoint);
-
-  /* Clean up */
-  proxy_info_destroy (pi);
-}
-
-static void
-proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data)
-{
-  struct proxy_info *pi = data;
-  const struct impl *impl = pi->impl;
-  WpProxyPort *proxy_port = NULL;
-  struct pw_proxy *proxy = NULL;
-
-  /* Get the proxy port */
-  proxy_port = wp_proxy_port_new_finish(initable, res, NULL);
-  if (!proxy_port)
-    return;
-
-  /* Forward the proxy port */
-  pi->proxy_port = proxy_port;
-
-  /* Get the node proxy */
-  proxy = wp_remote_pipewire_proxy_bind (impl->remote_pipewire, pi->node_id,
-      PW_TYPE_INTERFACE_Node);
-  if (!proxy)
-    return;
-
-  /* Create the proxy node asynchronically */
-  wp_proxy_node_new(pi->node_id, proxy, proxy_node_created, pi);
 }
 
 static void
-handle_node(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
+on_node_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
     gpointer d)
 {
   struct impl *impl = d;
   const struct spa_dict *props = p;
-  const gchar *media_class = NULL, *name = NULL;
-  struct endpoint_info *ei = NULL;
+  g_autoptr (WpCore) core = wp_module_get_core (impl->module);
+  const gchar *name, *media_class;
+  GVariantBuilder b;
+  g_autoptr (GVariant) endpoint_props = NULL;
 
   /* Make sure the node has properties */
   g_return_if_fail(props);
@@ -170,40 +64,20 @@ handle_node(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
   if (g_str_has_prefix (media_class, "Audio/DSP"))
     return;
 
-  /* Create the endpoint info */
-  ei = g_slice_new0 (struct endpoint_info);
-  ei->name = g_strdup(name);
-  ei->media_class = g_strdup(media_class);
-
-  /* Insert the alsa node info in the hash table */
-  g_hash_table_insert(impl->alsa_nodes_info, GINT_TO_POINTER (id), ei);
-}
-
-static void
-handle_port(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
-    gpointer d)
-{
-  struct impl *impl = d;
-  struct proxy_info *pi = NULL;
-  struct pw_proxy *proxy = NULL;
-
-  /* Only handle ports whose parent is an alsa node */
-  if (!g_hash_table_contains(impl->alsa_nodes_info, GINT_TO_POINTER (parent_id)))
-    return;
-
-  /* Get the port proxy */
-  proxy = wp_remote_pipewire_proxy_bind (rp, id, PW_TYPE_INTERFACE_Port);
-  if (!proxy)
-    return;
-
-  /* Create the port info */
-  pi = g_slice_new0 (struct proxy_info);
-  pi->impl = impl;
-  pi->node_id = parent_id;
-  pi->proxy_port = NULL;
+  /* Set the properties */
+  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&b, "{sv}",
+      "name", g_variant_new_take_string (g_strdup_printf (
+          "Endpoint %u: %s", id, name)));
+  g_variant_builder_add (&b, "{sv}",
+      "media-class", g_variant_new_string (media_class));
+  g_variant_builder_add (&b, "{sv}",
+      "global-id", g_variant_new_uint32 (id));
+  endpoint_props = g_variant_builder_end (&b);
 
-  /* Create the proxy port asynchronically */
-  wp_proxy_port_new(id, proxy, proxy_port_created, pi);
+  /* Create the endpoint async */
+  wp_factory_make_async (core, "pw-audio-softdsp-endpoint", WP_TYPE_ENDPOINT,
+      endpoint_props, on_endpoint_created, impl);
 }
 
 static void
@@ -215,10 +89,6 @@ module_destroy (gpointer data)
   impl->module = NULL;
   impl->remote_pipewire = NULL;
 
-  /* Destroy the hash table */
-  g_hash_table_unref (impl->alsa_nodes_info);
-  impl->alsa_nodes_info = NULL;
-
   /* Clean up */
   g_slice_free (struct impl, impl);
 }
@@ -241,13 +111,10 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
   impl = g_slice_new0(struct impl);
   impl->module = module;
   impl->remote_pipewire = rp;
-  impl->alsa_nodes_info = g_hash_table_new_full (g_direct_hash,
-      g_direct_equal, NULL, endpoint_info_destroy);
 
   /* Set destroy callback for impl */
   wp_module_set_destroy_callback (module, module_destroy, impl);
 
   /* Register the global addded callbacks */
-  g_signal_connect(rp, "global-added::node", (GCallback)handle_node, impl);
-  g_signal_connect(rp, "global-added::port", (GCallback)handle_port, impl);
+  g_signal_connect(rp, "global-added::node", (GCallback)on_node_added, impl);
 }
diff --git a/modules/module-pw-audio-softdsp-endpoint.c b/modules/module-pw-audio-softdsp-endpoint.c
index c728dce55b3193b51f6720b35751f89fd5879886..f901508ea9f68adc83e26193800a6dd7c736fcc8 100644
--- a/modules/module-pw-audio-softdsp-endpoint.c
+++ b/modules/module-pw-audio-softdsp-endpoint.c
@@ -26,6 +26,12 @@ struct _WpPwAudioSoftdspEndpoint
 {
   WpEndpoint parent;
 
+  /* The global-id this endpoint refers to */
+  guint global_id;
+
+  /* The task to signal the endpoint is initialized */
+  GTask *init_task;
+
   /* The remote pipewire */
   WpRemotePipewire *remote_pipewire;
 
@@ -36,28 +42,25 @@ struct _WpPwAudioSoftdspEndpoint
   /* Direction */
   enum pw_direction direction;
 
-  /* Proxy */
+  /* Proxies */
   WpProxyNode *proxy_node;
   WpProxyPort *proxy_port;
-
-  /* DSP port id */
-  uint32_t dsp_port_id;
+  WpProxyNode *proxy_dsp;
+  GPtrArray *proxies_dsp_port;
 
   /* Volume */
   gfloat master_volume;
   gboolean master_mute;
 
-  /* TODO: This needs to use the new proxy API */
-  struct pw_node_proxy *dsp_proxy;
+  /* DSP */
+  uint32_t dsp_port_id;
   struct spa_hook dsp_listener;
-  struct pw_node_info *dsp_info;
   struct pw_proxy *link_proxy;
 };
 
 enum {
   PROP_0,
-  PROP_NODE_PROXY,
-  PROP_PORT_PROXY,
+  PROP_GLOBAL_ID,
 };
 
 enum {
@@ -66,26 +69,33 @@ enum {
   CONTROL_SELECTED,
 };
 
+static GAsyncInitableIface *wp_endpoint_parent_interface = NULL;
+static void wp_endpoint_async_initable_init (gpointer iface,
+    gpointer iface_data);
+
 G_DECLARE_FINAL_TYPE (WpPwAudioSoftdspEndpoint, endpoint,
     WP_PW, AUDIO_SOFTDSP_ENDPOINT, WpEndpoint)
 
-G_DEFINE_TYPE (WpPwAudioSoftdspEndpoint, endpoint, WP_TYPE_ENDPOINT)
+G_DEFINE_TYPE_WITH_CODE (WpPwAudioSoftdspEndpoint, endpoint, WP_TYPE_ENDPOINT,
+    G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                           wp_endpoint_async_initable_init))
 
 static gboolean
 endpoint_prepare_link (WpEndpoint * ep, guint32 stream_id,
     WpEndpointLink * link, GVariant ** properties, GError ** error)
 {
   WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (ep);
+  const struct pw_node_info *dsp_info = NULL;
   GVariantBuilder b;
 
-  /* Make sure dsp info is valid */
-  if (!self->dsp_info)
-    return FALSE;
+  /* Get the dsp info */
+  dsp_info = wp_proxy_node_get_info(self->proxy_dsp);
+  g_return_val_if_fail (dsp_info, FALSE);
 
   /* Set the properties */
   g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
   g_variant_builder_add (&b, "{sv}", "node-id",
-      g_variant_new_uint32 (self->dsp_info->id));
+      g_variant_new_uint32 (dsp_info->id));
   g_variant_builder_add (&b, "{sv}", "node-port-id",
       g_variant_new_uint32 (self->dsp_port_id));
   *properties = g_variant_builder_end (&b);
@@ -98,15 +108,18 @@ on_dsp_running(WpPwAudioSoftdspEndpoint *self)
 {
   struct pw_properties *props;
   const struct pw_node_info *node_info = NULL;
+  const struct pw_node_info *dsp_info = NULL;
 
   /* Return if the node has already been linked */
-  if (self->link_proxy)
-    return;
+  g_return_if_fail (!self->link_proxy);
 
   /* Get the node info */
   node_info = wp_proxy_node_get_info(self->proxy_node);
-  if (!node_info)
-    return;
+  g_return_if_fail (node_info);
+
+  /* Get the dsp info */
+  dsp_info = wp_proxy_node_get_info(self->proxy_dsp);
+  g_return_if_fail (dsp_info);
 
   /* Create new properties */
   props = pw_properties_new(NULL, NULL);
@@ -114,14 +127,14 @@ on_dsp_running(WpPwAudioSoftdspEndpoint *self)
   /* Set the new properties */
   pw_properties_set(props, PW_LINK_PROP_PASSIVE, "true");
   if (self->direction == PW_DIRECTION_OUTPUT) {
-    pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", self->dsp_info->id);
+    pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", dsp_info->id);
     pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", -1);
     pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", node_info->id);
     pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", -1);
   } else {
     pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", node_info->id);
     pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", -1);
-    pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", self->dsp_info->id);
+    pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", dsp_info->id);
     pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", -1);
   }
 
@@ -150,9 +163,6 @@ dsp_node_event_info (void *data, const struct pw_node_info *info)
 {
   WpPwAudioSoftdspEndpoint *self = data;
 
-  /* Set dsp info */
-  self->dsp_info = pw_node_info_update(self->dsp_info, info);
-
   /* Handle the different states */
   switch (info->state) {
   case PW_NODE_STATE_IDLE:
@@ -221,32 +231,88 @@ static const struct pw_node_proxy_events dsp_node_events = {
 };
 
 static void
-emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self)
+on_proxy_node_destroyed (WpProxy* wp_proxy, WpEndpoint *endpoint)
 {
-  struct pw_properties *props;
-  const char *dsp_name = NULL;
+  g_return_if_fail(WP_IS_ENDPOINT(endpoint));
+
+  /* Unregister the endpoint */
+  wp_endpoint_unregister(endpoint);
+}
+
+static void
+on_proxy_dsp_done(WpProxy *proxy, gpointer data)
+{
+  WpPwAudioSoftdspEndpoint *self = data;
+
+  /* Don't do anything if the endpoint has already been initialized */
+  if (!self->init_task)
+    return;
+
+  /* Set destroy handler to unregister endpoint on proxy_node destruction */
+  g_signal_connect (self->proxy_node, "destroyed",
+      G_CALLBACK(on_proxy_node_destroyed), WP_ENDPOINT(self));
+
+  /* Finish the creation of the endpoint */
+  g_task_return_boolean (self->init_task, TRUE);
+  g_clear_object(&self->init_task);
+}
+
+static void
+on_proxy_dsp_created(GObject *initable, GAsyncResult *res, gpointer data)
+{
+  WpPwAudioSoftdspEndpoint *self = data;
+  struct pw_node_proxy *dsp_proxy = NULL;
+  const struct spa_audio_info_raw *port_format;
+  struct spa_audio_info_raw format;
   uint8_t buf[1024];
   struct spa_pod_builder pod_builder = { 0, };
   struct spa_pod *param;
-  const struct pw_node_info *node_info;
-  const struct spa_audio_info_raw *port_format;
-  struct spa_audio_info_raw format;
 
-  /* Get the node info */
-  node_info = wp_proxy_node_get_info(self->proxy_node);
-  if (!node_info)
-    return;
+  /* Get the proxy dsp */
+  self->proxy_dsp = wp_proxy_node_new_finish(initable, res, NULL);
+  g_return_if_fail (self->proxy_dsp);
+
+  /* Add a custom dsp listener */
+  dsp_proxy = wp_proxy_get_pw_proxy(WP_PROXY(self->proxy_dsp));
+  g_return_if_fail (dsp_proxy);
+  pw_node_proxy_add_listener(dsp_proxy, &self->dsp_listener,
+      &dsp_node_events, self);
+
+  /* Emit the props param */
+  pw_node_proxy_enum_params (dsp_proxy, 0, SPA_PARAM_Props, 0, -1, NULL);
 
   /* Get the port format */
   port_format = wp_proxy_port_get_format(self->proxy_port);
-  if (!port_format)
-    return;
+  g_return_if_fail (port_format);
   format = *port_format;
 
+  /* Build the param profile */
+  spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
+  param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
+  param = spa_pod_builder_add_object(&pod_builder,
+      SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
+      SPA_PARAM_PROFILE_direction,  SPA_POD_Id(pw_direction_reverse(self->direction)),
+      SPA_PARAM_PROFILE_format,     SPA_POD_Pod(param));
+
+  /* Set the param profile to emit the dsp ports */
+  pw_node_proxy_set_param(dsp_proxy, SPA_PARAM_Profile, 0, param);
+}
+
+static void
+emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self)
+{
+  struct pw_properties *props;
+  const char *dsp_name = NULL;
+  struct pw_node_proxy *dsp_proxy = NULL;
+  const struct pw_node_info *node_info;
+
+  /* Get the node info */
+  node_info = wp_proxy_node_get_info(self->proxy_node);
+  g_return_if_fail (node_info);
+
   /* Get the properties */
   props = pw_properties_new_dict(node_info->props);
-  if (!props)
-    return;
+  g_return_if_fail (props);
 
   /* Get the DSP name */
   dsp_name = pw_properties_get(props, "device.nick");
@@ -259,87 +325,25 @@ emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self)
   pw_properties_setf(props, "audio-dsp.maxbuffer", "%ld",
       MAX_QUANTUM_SIZE * sizeof(float));
 
-  /* Set the DSP proxy and listener */
-  self->dsp_proxy = wp_remote_pipewire_create_object(self->remote_pipewire,
+  /* Create the proxy dsp async */
+  dsp_proxy = wp_remote_pipewire_create_object(self->remote_pipewire,
       "audio-dsp", PW_TYPE_INTERFACE_Node, &props->dict);
-  pw_node_proxy_add_listener(self->dsp_proxy, &self->dsp_listener,
-      &dsp_node_events, self);
-  pw_node_proxy_enum_params (self->dsp_proxy, 0, SPA_PARAM_Props, 0, -1, NULL);
-
-  /* Set DSP proxy params */
-  spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
-  param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
-  param = spa_pod_builder_add_object(&pod_builder,
-      SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
-      SPA_PARAM_PROFILE_direction,  SPA_POD_Id(pw_direction_reverse(self->direction)),
-      SPA_PARAM_PROFILE_format,     SPA_POD_Pod(param));
-  pw_node_proxy_set_param((struct pw_node_proxy*)self->dsp_proxy,
-      SPA_PARAM_Profile, 0, param);
+  wp_proxy_node_new(pw_proxy_get_id((struct pw_proxy *)dsp_proxy), dsp_proxy,
+      on_proxy_dsp_created, self);
 
   /* Clean up */
   pw_properties_free(props);
 }
 
 static void
-handle_port(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
-    gpointer d)
-{
-  WpPwAudioSoftdspEndpoint *self = d;
-  const struct spa_dict *props = p;
-  const char *direction_prop = NULL;
-  enum pw_direction direction;
-
-  /* Make sure the dsp port is not already set*/
-  if (self->dsp_port_id != 0)
-    return;
-
-  /* Make sure the port has porperties */
-  if (!props)
-    return;
-
-  /* Only handle ports owned by this endpoint */
-  if (!self->dsp_info || self->dsp_info->id != parent_id)
-    return;
-
-  /* Get the direction property */
-  direction_prop = spa_dict_lookup(props, "port.direction");
-  if (!direction_prop)
-    return;
-  direction =
-      !strcmp(direction_prop, "out") ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT;
-
-  /* Only handle ports with the oposit direction of the endpoint */
-  if (self->direction == direction)
-    return;
-
-  /* Set the dsp port id */
-  self->dsp_port_id = id;
-}
-
-static void
-endpoint_constructed (GObject * object)
+on_proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data)
 {
-  WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object);
-  g_autoptr (WpCore) core = wp_endpoint_get_core(WP_ENDPOINT(self));
-  const gchar *media_class = wp_endpoint_get_media_class (WP_ENDPOINT (self));
+  WpPwAudioSoftdspEndpoint *self = data;
   GVariantDict d;
 
-  /* Set Remote Pipewire */
-  self->remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE);
-  if (!self->remote_pipewire)
-    g_critical ("failed to get remote pipewire ");
-
-  /* Register the global-added::port callback in remote pipewire */
-  g_signal_connect(self->remote_pipewire, "global-added::port",
-      (GCallback)handle_port, self);
-
-  /* Set the direction */
-  if (g_str_has_suffix (media_class, "Source"))
-    self->direction = PW_DIRECTION_INPUT;
-  else if (g_str_has_suffix (media_class, "Sink"))
-    self->direction = PW_DIRECTION_OUTPUT;
-  else
-    g_critical ("failed to parse direction");
+  /* Get the proxy node */
+  self->proxy_node = wp_proxy_node_new_finish(initable, res, NULL);
+  g_return_if_fail (self->proxy_node);
 
   /* Emit the audio DSP node */
   emit_audio_dsp_node(self);
@@ -373,37 +377,143 @@ endpoint_constructed (GObject * object)
   g_variant_dict_insert (&d, "type", "s", "b");
   g_variant_dict_insert (&d, "default-value", "b", self->selected);
   wp_endpoint_register_control (WP_ENDPOINT (self), g_variant_dict_end (&d));
+}
+
+static void
+on_proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data)
+{
+  WpPwAudioSoftdspEndpoint *self = data;
+  struct pw_node_proxy *node_proxy = NULL;
+
+  /* Get the proxy port */
+  self->proxy_port = wp_proxy_port_new_finish(initable, res, NULL);
+  g_return_if_fail (self->proxy_port);
 
-  G_OBJECT_CLASS (endpoint_parent_class)->constructed (object);
+  /* Create the proxy node async */
+  node_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire,
+      self->global_id, PW_TYPE_INTERFACE_Node);
+  g_return_if_fail(node_proxy);
+  wp_proxy_node_new(self->global_id, node_proxy, on_proxy_node_created, self);
 }
 
 static void
-endpoint_finalize (GObject * object)
+handle_node_port(WpPwAudioSoftdspEndpoint *self, guint id, guint parent_id,
+  const struct spa_dict *props)
 {
-  WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object);
+  struct pw_port_proxy *port_proxy = NULL;
 
-  /* Set to NULL remote pipewire as we don't own the reference */
-  self->remote_pipewire = NULL;
+  /* Alsa nodes should have 1 port only, so make sure proxy_port is not set */
+  if (self->proxy_port != 0)
+    return;
 
-  /* Unref the proxy node */
-  g_clear_object (&self->proxy_node);
+  /* Create the proxy port async */
+  port_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire, id,
+    PW_TYPE_INTERFACE_Port);
+  g_return_if_fail(port_proxy);
+  wp_proxy_port_new(id, port_proxy, on_proxy_port_created, self);
+}
 
-  /* Unref the proxy port */
-  g_clear_object (&self->proxy_port);
+static void
+on_proxy_dsp_port_created(GObject *initable, GAsyncResult *res, gpointer data)
+{
+  WpPwAudioSoftdspEndpoint *self = data;
+  WpProxyPort *proxy_dsp_port = NULL;
 
-  /* Clear the dsp info */
-  if (self->dsp_info) {
-    pw_node_info_free(self->dsp_info);
-    self->dsp_info = NULL;
+  /* Get the proxy dsp port */
+  proxy_dsp_port = wp_proxy_port_new_finish(initable, res, NULL);
+  g_return_if_fail (proxy_dsp_port);
+
+  /* Add the proxy dsp port to the array */
+  g_return_if_fail (self->proxies_dsp_port);
+  g_ptr_array_add(self->proxies_dsp_port, proxy_dsp_port);
+
+  /* Register a callback to know when all the dsp ports have been emitted */
+  g_signal_connect(self->proxy_dsp, "done", (GCallback)on_proxy_dsp_done, self);
+  wp_proxy_sync (WP_PROXY(self->proxy_dsp));
+}
+
+static void
+handle_dsp_port(WpPwAudioSoftdspEndpoint *self, guint id, guint parent_id,
+  const struct spa_dict *props)
+{
+  const char *direction_prop = NULL;
+  struct pw_port_proxy *port_proxy = NULL;
+  enum pw_direction direction;
+
+  /* Create the proxy dsp port async */
+  port_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire, id,
+      PW_TYPE_INTERFACE_Port);
+  g_return_if_fail(port_proxy);
+  wp_proxy_port_new(id, port_proxy, on_proxy_dsp_port_created, self);
+
+  /* Make sure the port has porperties */
+  g_return_if_fail(props);
+
+  /* TODO: For now we only handle 1 DSP port */
+  if (self->dsp_port_id != 0)
+    return;
+
+  /* Get the direction property */
+  direction_prop = spa_dict_lookup(props, "port.direction");
+  if (!direction_prop)
+    return;
+  direction =
+      !strcmp(direction_prop, "out") ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT;
+
+  /* Only handle ports with the opposite direction of the endpoint */
+  if (self->direction == direction)
+    return;
+
+  /* Set the dsp port id */
+  self->dsp_port_id = id;
+}
+
+static void
+on_port_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p,
+    gpointer d)
+{
+  WpPwAudioSoftdspEndpoint *self = d;
+  const struct spa_dict *props = p;
+  const struct pw_node_info *dsp_info = NULL;
+
+  /* Check if it is a node port and handle it */
+  if (self->global_id == parent_id) {
+    handle_node_port(self, id, parent_id, props);
+    return;
   }
 
-  /* Destroy the dsp_proxy */
-  if (self->dsp_proxy) {
-    spa_hook_remove (&self->dsp_listener);
-    pw_proxy_destroy ((struct pw_proxy *) self->dsp_proxy);
-    self->dsp_proxy = NULL;
+  /* Otherwise, check if it is a dsp port and handle it */
+  if (!self->proxy_dsp)
+    return;
+  dsp_info = wp_proxy_node_get_info (self->proxy_dsp);
+  if (!dsp_info || dsp_info->id != parent_id)
+    return;
+  handle_dsp_port(self, id, parent_id, props);
+}
+
+static void
+endpoint_finalize (GObject * object)
+{
+  WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object);
+
+  /* Destroy the proxies port */
+  if (self->proxies_dsp_port) {
+    g_ptr_array_free(self->proxies_dsp_port, TRUE);
+    self->proxies_dsp_port = NULL;
   }
 
+  /* Destroy the proxy node */
+  g_clear_object(&self->proxy_node);
+
+  /* Destroy the proxy port */
+  g_clear_object(&self->proxy_port);
+
+  /* Destroy the proxy dsp */
+  g_clear_object(&self->proxy_dsp);
+
+  /* Destroy the done task */
+  g_clear_object(&self->init_task);
+
   G_OBJECT_CLASS (endpoint_parent_class)->finalize (object);
 }
 
@@ -414,13 +524,8 @@ endpoint_set_property (GObject * object, guint property_id,
   WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object);
 
   switch (property_id) {
-  case PROP_NODE_PROXY:
-    g_clear_object(&self->proxy_node);
-    self->proxy_node = g_value_dup_object(value);
-    break;
-  case PROP_PORT_PROXY:
-    g_clear_object(&self->proxy_port);
-    self->proxy_port = g_value_dup_object(value);
+  case PROP_GLOBAL_ID:
+    self->global_id = g_value_get_uint(value);
     break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -435,11 +540,8 @@ endpoint_get_property (GObject * object, guint property_id,
   WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object);
 
   switch (property_id) {
-  case PROP_NODE_PROXY:
-    g_value_set_object (value, self->proxy_node);
-    break;
-  case PROP_PORT_PROXY:
-    g_value_set_object (value, self->proxy_port);
+  case PROP_GLOBAL_ID:
+    g_value_set_uint (value, self->global_id);
     break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -472,14 +574,14 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id,
   WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (ep);
   char buf[1024];
   struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
+  struct pw_node_proxy *dsp_proxy = NULL;
   float volume;
   bool mute;
 
-  if (!self->dsp_proxy) {
-    g_debug("WpEndpoint:%p too early to set control, dsp is not created yet",
-        self);
-    return FALSE;
-  }
+  /* Get the pipewire dsp proxy */
+  g_return_val_if_fail (self->proxy_dsp, FALSE);
+  dsp_proxy = wp_proxy_get_pw_proxy (WP_PROXY(self->proxy_dsp));
+  g_return_val_if_fail (dsp_proxy, FALSE);
 
   switch (control_id) {
     case CONTROL_VOLUME:
@@ -488,13 +590,13 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id,
       g_debug("WpEndpoint:%p set volume control (%u) value, vol:%f", self,
           control_id, volume);
 
-      pw_node_proxy_set_param (self->dsp_proxy,
+      pw_node_proxy_set_param (dsp_proxy,
           SPA_PARAM_Props, 0,
           spa_pod_builder_add_object (&b,
               SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
               SPA_PROP_volume, SPA_POD_Float(volume),
               NULL));
-      pw_node_proxy_enum_params (self->dsp_proxy, 0, SPA_PARAM_Props, 0, -1,
+      pw_node_proxy_enum_params (dsp_proxy, 0, SPA_PARAM_Props, 0, -1,
           NULL);
       break;
 
@@ -504,13 +606,13 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id,
       g_debug("WpEndpoint:%p set mute control (%u) value, mute:%d", self,
           control_id, mute);
 
-      pw_node_proxy_set_param (self->dsp_proxy,
+      pw_node_proxy_set_param (dsp_proxy,
           SPA_PARAM_Props, 0,
           spa_pod_builder_add_object (&b,
               SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
               SPA_PROP_mute, SPA_POD_Bool(mute),
               NULL));
-      pw_node_proxy_enum_params (self->dsp_proxy, 0, SPA_PARAM_Props, 0, -1,
+      pw_node_proxy_enum_params (dsp_proxy, 0, SPA_PARAM_Props, 0, -1,
           NULL);
       break;
 
@@ -527,6 +629,51 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id,
   return TRUE;
 }
 
+static void
+wp_endpoint_init_async (GAsyncInitable *initable, int io_priority,
+    GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
+{
+  WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (initable);
+  g_autoptr (WpCore) core = wp_endpoint_get_core(WP_ENDPOINT(self));
+  const gchar *media_class = wp_endpoint_get_media_class (WP_ENDPOINT (self));
+
+  /* Create the async task */
+  self->init_task = g_task_new (initable, cancellable, callback, data);
+
+  /* Init the proxies_dsp_port array */
+  self->proxies_dsp_port = g_ptr_array_new_full(4, (GDestroyNotify)g_object_unref);
+
+  /* Set the direction */
+  if (g_str_has_suffix (media_class, "Source"))
+    self->direction = PW_DIRECTION_INPUT;
+  else if (g_str_has_suffix (media_class, "Sink"))
+    self->direction = PW_DIRECTION_OUTPUT;
+  else
+    g_critical ("failed to parse direction");
+
+  /* Register a port_added callback */
+  self->remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE);
+  g_return_if_fail(self->remote_pipewire);
+  g_signal_connect(self->remote_pipewire, "global-added::port",
+      (GCallback)on_port_added, self);
+
+  /* Call the parent interface */
+  wp_endpoint_parent_interface->init_async (initable, io_priority, cancellable,
+      callback, data);
+}
+
+static void
+wp_endpoint_async_initable_init (gpointer iface, gpointer iface_data)
+{
+  GAsyncInitableIface *ai_iface = iface;
+
+  /* Set the parent interface */
+  wp_endpoint_parent_interface = g_type_interface_peek_parent (iface);
+
+  /* Only set the init_async */
+  ai_iface->init_async = wp_endpoint_init_async;
+}
+
 static void
 endpoint_init (WpPwAudioSoftdspEndpoint * self)
 {
@@ -538,7 +685,6 @@ endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass)
   GObjectClass *object_class = (GObjectClass *) klass;
   WpEndpointClass *endpoint_class = (WpEndpointClass *) klass;
 
-  object_class->constructed = endpoint_constructed;
   object_class->finalize = endpoint_finalize;
   object_class->set_property = endpoint_set_property;
   object_class->get_property = endpoint_get_property;
@@ -548,48 +694,42 @@ endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass)
   endpoint_class->set_control_value = endpoint_set_control_value;
 
   /* Instal the properties */
-  g_object_class_install_property (object_class, PROP_NODE_PROXY,
-      g_param_spec_object ("node-proxy", "node-proxy",
-          "Pointer to the node proxy of the device", WP_TYPE_PROXY_NODE,
-          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-  g_object_class_install_property (object_class, PROP_PORT_PROXY,
-      g_param_spec_object ("port-proxy", "port-proxy",
-          "Pointer to the port proxy of the device", WP_TYPE_PROXY_PORT,
+  g_object_class_install_property (object_class, PROP_GLOBAL_ID,
+      g_param_spec_uint ("global-id", "global-id",
+          "The global Id this endpoint refers to", 0, G_MAXUINT, 0,
           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 }
 
-static gpointer
-endpoint_factory (WpFactory * factory, GType type, GVariant * properties)
+void
+endpoint_factory (WpFactory * factory, GType type, GVariant * properties,
+  GAsyncReadyCallback ready, gpointer user_data)
 {
   g_autoptr (WpCore) core = NULL;
-  const gchar *name = NULL;
-  const gchar *media_class = NULL;
-  guint64 proxy_node, proxy_port;
+  const gchar *name, *media_class;
+  guint global_id;
 
   /* Make sure the type is correct */
-  g_return_val_if_fail(type == WP_TYPE_ENDPOINT, NULL);
+  g_return_if_fail(type == WP_TYPE_ENDPOINT);
 
   /* Get the Core */
   core = wp_factory_get_core(factory);
-  g_return_val_if_fail (core, NULL);
+  g_return_if_fail (core);
 
   /* Get the properties */
   if (!g_variant_lookup (properties, "name", "&s", &name))
-      return NULL;
+      return;
   if (!g_variant_lookup (properties, "media-class", "&s", &media_class))
-      return NULL;
-  if (!g_variant_lookup (properties, "proxy-node", "t", &proxy_node))
-      return NULL;
-  if (!g_variant_lookup (properties, "proxy-port", "t", &proxy_port))
-      return NULL;
+      return;
+  if (!g_variant_lookup (properties, "global-id", "u", &global_id))
+      return;
 
   /* Create and return the softdsp endpoint object */
-  return g_object_new (endpoint_get_type (),
+  g_async_initable_new_async (
+      endpoint_get_type (), G_PRIORITY_DEFAULT, NULL, ready, user_data,
       "core", core,
       "name", name,
       "media-class", media_class,
-      "node-proxy", (gpointer) proxy_node,
-      "port-proxy", (gpointer) proxy_port,
+      "global-id", global_id,
       NULL);
 }
 
@@ -597,5 +737,5 @@ void
 wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
 {
   /* Register the softdsp endpoint */
-  wp_factory_new (core, "pw-audio-softdsp-endpoint", endpoint_factory);
+  wp_factory_new_async (core, "pw-audio-softdsp-endpoint", endpoint_factory);
 }