diff --git a/res/ari/resource_events.c b/res/ari/resource_events.c
index eddcb662c94694c047c73b938bc402380274846e..e666f2e05866b889b6d648f52940bbad64fe06db 100644
--- a/res/ari/resource_events.c
+++ b/res/ari/resource_events.c
@@ -119,6 +119,10 @@ static void app_handler(void *data, const char *app_name,
 	const char *msg_application = S_OR(
 		ast_json_string_get(ast_json_object_get(message, "application")),
 		"");
+
+	if (!session) {
+		return;
+	}
  
 	/* Determine if we've been replaced */
 	if (strcmp(msg_type, "ApplicationReplaced") == 0 &&
@@ -168,7 +172,40 @@ static int session_register_app(struct event_session *session,
 	return 0;
 }
 
-void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *ws_session,
+int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser,
+	struct ast_variable *headers,
+	struct ast_ari_events_event_websocket_args *args)
+{
+	int res = 0;
+	size_t i, j;
+
+	ast_debug(3, "/events WebSocket attempted\n");
+
+	if (args->app_count == 0) {
+		ast_http_error(ser, 400, "Bad Request", "Missing param 'app'");
+		return -1;
+	}
+
+	for (i = 0; i < args->app_count; ++i) {
+		if (ast_strlen_zero(args->app[i])) {
+			res = -1;
+			break;
+		}
+
+		res |= stasis_app_register(args->app[i], app_handler, NULL);
+	}
+
+	if (res) {
+		for (j = 0; j < i; ++j) {
+			stasis_app_unregister(args->app[j]);
+		}
+		ast_http_error(ser, 400, "Bad Request", "Invalid application provided in param 'app'.");
+	}
+
+	return res;
+}
+
+void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *ws_session,
 	struct ast_variable *headers,
 	struct ast_ari_events_event_websocket_args *args)
 {
diff --git a/res/ari/resource_events.h b/res/ari/resource_events.h
index 646cf9bfceb763fa27cc7f817bcd169b0871f12f..2b631819b266ac93fa9cb23564ed58d778f03db3 100644
--- a/res/ari/resource_events.h
+++ b/res/ari/resource_events.h
@@ -48,6 +48,19 @@ struct ast_ari_events_event_websocket_args {
 	/*! Parsing context for app. */
 	char *app_parse;
 };
+
+/*!
+ * \brief WebSocket connection for events.
+ *
+ * \param ser HTTP TCP/TLS Server Session
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ *
+ * \retval 0 success
+ * \retval non-zero error
+ */
+int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
+
 /*!
  * \brief WebSocket connection for events.
  *
@@ -55,7 +68,7 @@ struct ast_ari_events_event_websocket_args {
  * \param headers HTTP headers.
  * \param args Swagger parameters.
  */
-void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
+void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args);
 /*! Argument struct for ast_ari_events_user_event() */
 struct ast_ari_events_user_event_args {
 	/*! Event name */
diff --git a/res/res_ari_events.c b/res/res_ari_events.c
index 66b01a743d998fa77dbce78fcc6fb3dd09de7d81..454233945e7f234e208667b0e83df69868bef5d9 100644
--- a/res/res_ari_events.c
+++ b/res/res_ari_events.c
@@ -53,7 +53,92 @@ ASTERISK_REGISTER_FILE()
 
 #define MAX_VALS 128
 
-static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_session,
+static int ast_ari_events_event_websocket_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
+{
+	struct ast_ari_events_event_websocket_args args = {};
+	int res = 0;
+	RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
+	struct ast_variable *i;
+
+	response = ast_calloc(1, sizeof(*response));
+	if (!response) {
+		ast_log(LOG_ERROR, "Failed to create response.\n");
+		goto fin;
+	}
+
+	for (i = get_params; i; i = i->next) {
+		if (strcmp(i->name, "app") == 0) {
+			/* Parse comma separated list */
+			char *vals[MAX_VALS];
+			size_t j;
+
+			args.app_parse = ast_strdup(i->value);
+			if (!args.app_parse) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			if (strlen(args.app_parse) == 0) {
+				/* ast_app_separate_args can't handle "" */
+				args.app_count = 1;
+				vals[0] = args.app_parse;
+			} else {
+				args.app_count = ast_app_separate_args(
+					args.app_parse, ',', vals,
+					ARRAY_LEN(vals));
+			}
+
+			if (args.app_count == 0) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			if (args.app_count >= MAX_VALS) {
+				ast_ari_response_error(response, 400,
+					"Bad Request",
+					"Too many values for app");
+				goto fin;
+			}
+
+			args.app = ast_malloc(sizeof(*args.app) * args.app_count);
+			if (!args.app) {
+				ast_ari_response_alloc_failed(response);
+				goto fin;
+			}
+
+			for (j = 0; j < args.app_count; ++j) {
+				args.app[j] = (vals[j]);
+			}
+		} else
+		{}
+	}
+
+	res = ast_ari_websocket_events_event_websocket_attempted(ser, headers, &args);
+
+fin: __attribute__((unused))
+	if (!response) {
+		ast_http_error(ser, 500, "Server Error", "Memory allocation error");
+		res = -1;
+	} else if (response->response_code != 0) {
+		/* Param parsing failure */
+		RAII_VAR(char *, msg, NULL, ast_json_free);
+		if (response->message) {
+			msg = ast_json_dump_string(response->message);
+		} else {
+			ast_log(LOG_ERROR, "Missing response message\n");
+		}
+
+		if (msg) {
+			ast_http_error(ser, response->response_code, response->response_text, msg);
+		}
+		res = -1;
+	}
+	ast_free(args.app_parse);
+	ast_free(args.app);
+	return res;
+}
+
+static void ast_ari_events_event_websocket_ws_established_cb(struct ast_websocket *ws_session,
 	struct ast_variable *get_params, struct ast_variable *headers)
 {
 	struct ast_ari_events_event_websocket_args args = {};
@@ -126,16 +211,11 @@ static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_sessio
 		{}
 	}
 
-	ast_ari_websocket_events_event_websocket(session, headers, &args);
+	ast_ari_websocket_events_event_websocket_established(session, headers, &args);
 
 fin: __attribute__((unused))
 	if (response && response->response_code != 0) {
 		/* Param parsing failure */
-		/* TODO - ideally, this would return the error code to the
-		 * HTTP client; but we've already done the WebSocket
-		 * negotiation. Param parsing should happen earlier, but we
-		 * need a way to pass it through the WebSocket code to the
-		 * callback */
 		RAII_VAR(char *, msg, NULL, ast_json_free);
 		if (response->message) {
 			msg = ast_json_dump_string(response->message);
@@ -351,12 +431,22 @@ static struct stasis_rest_handlers events = {
 static int load_module(void)
 {
 	int res = 0;
+	struct ast_websocket_protocol *protocol;
+
 	events.ws_server = ast_websocket_server_create();
 	if (!events.ws_server) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
-	res |= ast_websocket_server_add_protocol(events.ws_server,
-		"ari", ast_ari_events_event_websocket_ws_cb);
+
+	protocol = ast_websocket_sub_protocol_alloc("ari");
+	if (!protocol) {
+		ao2_ref(events.ws_server, -1);
+		events.ws_server = NULL;
+		return AST_MODULE_LOAD_FAILURE;
+	}
+	protocol->session_attempted = ast_ari_events_event_websocket_ws_attempted_cb;
+	protocol->session_established = ast_ari_events_event_websocket_ws_established_cb;
+	res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
 	stasis_app_ref();
 	res |= ast_ari_add_handler(&events);
 	return res;
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 5a1c5f8e24b121fe46d76e2a221ba87f381a2e10..b99e23205e560fef439f0854bdb1a5048b245329 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -871,8 +871,7 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat
 
 	strncpy(app->name, name, size - sizeof(*app));
 	app->handler = handler;
-	ao2_ref(data, +1);
-	app->data = data;
+	app->data = ao2_bump(data);
 
 	ao2_ref(app, +1);
 	return app;
@@ -950,7 +949,7 @@ void app_update(struct stasis_app *app, stasis_app_cb handler, void *data)
 {
 	SCOPED_AO2LOCK(lock, app);
 
-	if (app->handler) {
+	if (app->handler && app->data) {
 		RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 
 		ast_verb(1, "Replacing Stasis app '%s'\n", app->name);
diff --git a/rest-api-templates/ari_resource.h.mustache b/rest-api-templates/ari_resource.h.mustache
index 3a20776a77a11b2c1dade8dd18dfe5dc98b8753f..d3f40b6bd65e2e21b9a0edaa7092940777242726 100644
--- a/rest-api-templates/ari_resource.h.mustache
+++ b/rest-api-templates/ari_resource.h.mustache
@@ -89,6 +89,23 @@ int ast_ari_{{c_name}}_{{c_nickname}}_parse_body(
 void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args, struct ast_ari_response *response);
 {{/is_req}}
 {{#is_websocket}}
+
+/*!
+ * \brief {{summary}}
+{{#notes}}
+ *
+ * {{{notes}}}
+{{/notes}}
+ *
+ * \param ser HTTP TCP/TLS Server Session
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ *
+ * \retval 0 success
+ * \retval non-zero error
+ */
+int ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
+
 /*!
  * \brief {{summary}}
 {{#notes}}
@@ -100,7 +117,7 @@ void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_
  * \param headers HTTP headers.
  * \param args Swagger parameters.
  */
-void ast_ari_websocket_{{c_name}}_{{c_nickname}}(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
+void ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
 {{/is_websocket}}
 {{/operations}}
 {{/apis}}
diff --git a/rest-api-templates/res_ari_resource.c.mustache b/rest-api-templates/res_ari_resource.c.mustache
index 7d138b7af25b2e48f71e065db26cd77fd4d8897f..7fe360e896c75983299e8d4bc2714fa3165948ea 100644
--- a/rest-api-templates/res_ari_resource.c.mustache
+++ b/rest-api-templates/res_ari_resource.c.mustache
@@ -137,7 +137,52 @@ fin: __attribute__((unused))
 }
 {{/is_req}}
 {{#is_websocket}}
-static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_session,
+static int ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
+{
+	struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
+{{#has_parameters}}
+	int res = 0;
+	RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
+	struct ast_variable *i;
+{{/has_parameters}}
+
+{{#has_parameters}}
+	response = ast_calloc(1, sizeof(*response));
+	if (!response) {
+		ast_log(LOG_ERROR, "Failed to create response.\n");
+		goto fin;
+	}
+{{/has_parameters}}
+
+{{> param_parsing}}
+
+	res = ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(ser, headers, &args);
+
+fin: __attribute__((unused))
+	if (!response) {
+		ast_http_error(ser, 500, "Server Error", "Memory allocation error");
+		res = -1;
+	} else if (response->response_code != 0) {
+		/* Param parsing failure */
+		RAII_VAR(char *, msg, NULL, ast_json_free);
+		if (response->message) {
+			msg = ast_json_dump_string(response->message);
+		} else {
+			ast_log(LOG_ERROR, "Missing response message\n");
+		}
+
+		if (msg) {
+			ast_http_error(ser, response->response_code, response->response_text, msg);
+		}
+		res = -1;
+	}
+{{> param_cleanup}}
+{{#has_parameters}}
+	return res;
+{{/has_parameters}}
+}
+
+static void ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb(struct ast_websocket *ws_session,
 	struct ast_variable *get_params, struct ast_variable *headers)
 {
 	struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
@@ -175,16 +220,11 @@ static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_ses
 
 {{> param_parsing}}
 
-	ast_ari_websocket_{{c_name}}_{{c_nickname}}(session, headers, &args);
+	ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(session, headers, &args);
 
 fin: __attribute__((unused))
 	if (response && response->response_code != 0) {
 		/* Param parsing failure */
-		/* TODO - ideally, this would return the error code to the
-		 * HTTP client; but we've already done the WebSocket
-		 * negotiation. Param parsing should happen earlier, but we
-		 * need a way to pass it through the WebSocket code to the
-		 * callback */
 		RAII_VAR(char *, msg, NULL, ast_json_free);
 		if (response->message) {
 			msg = ast_json_dump_string(response->message);
@@ -211,16 +251,26 @@ static int load_module(void)
 {
 	int res = 0;
 {{#apis}}
+{{#operations}}
 {{#has_websocket}}
+	struct ast_websocket_protocol *protocol;
+
 	{{full_name}}.ws_server = ast_websocket_server_create();
 	if (!{{full_name}}.ws_server) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
+
+	protocol = ast_websocket_sub_protocol_alloc("{{websocket_protocol}}");
+	if (!protocol) {
+		ao2_ref({{full_name}}.ws_server, -1);
+		{{full_name}}.ws_server = NULL;
+		return AST_MODULE_LOAD_FAILURE;
+	}
+	protocol->session_attempted = ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb;
+	protocol->session_established = ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb;
 {{/has_websocket}}
-{{#operations}}
 {{#is_websocket}}
-	res |= ast_websocket_server_add_protocol({{full_name}}.ws_server,
-		"{{websocket_protocol}}", ast_ari_{{c_name}}_{{c_nickname}}_ws_cb);
+	res |= ast_websocket_server_add_protocol2({{full_name}}.ws_server, protocol);
 {{/is_websocket}}
 {{/operations}}
 {{/apis}}