diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index 0c22a6c309764c7adce9b9ab5b6a1191ca1de629..56e039b43eea4b107f7843f616d1608745968807 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -277,6 +277,60 @@ enum stasis_app_subscribe_res stasis_app_unsubscribe(const char *app_name,
 /*! \brief Handler for controlling a channel that's in a Stasis application */
 struct stasis_app_control;
 
+/*! \brief Rule to check to see if an operation is allowed */
+struct stasis_app_control_rule {
+	/*!
+	 * \brief Checks to see if an operation is allowed on the control
+	 *
+	 * \param control Control object to check
+	 * \return 0 on success, otherwise a failure code
+	 */
+	enum stasis_app_control_channel_result (*check_rule)(
+		const struct stasis_app_control *control);
+	/*! Next item in the list */
+	AST_LIST_ENTRY(stasis_app_control_rule) next;
+};
+
+/*!
+ * \brief Registers an add channel to bridge rule.
+ *
+ * \param control Control object
+ * \param rule The rule to register
+ */
+void stasis_app_control_register_add_rule(
+	struct stasis_app_control *control,
+	struct stasis_app_control_rule *rule);
+
+/*!
+ * \brief UnRegister an add channel to bridge rule.
+ *
+ * \param control Control object
+ * \param rule The rule to unregister
+ */
+void stasis_app_control_unregister_add_rule(
+	struct stasis_app_control *control,
+	struct stasis_app_control_rule *rule);
+
+/*!
+ * \brief Registers a remove channel from bridge rule.
+ *
+ * \param control Control object
+ * \param rule The rule to register
+ */
+void stasis_app_control_register_remove_rule(
+	struct stasis_app_control *control,
+	struct stasis_app_control_rule *rule);
+
+/*!
+ * \brief Unregisters a remove channel from bridge rule.
+ *
+ * \param control Control object
+ * \param rule The rule to unregister
+ */
+void stasis_app_control_unregister_remove_rule(
+	struct stasis_app_control *control,
+	struct stasis_app_control_rule *rule);
+
 /*!
  * \brief Returns the handler for the given channel.
  * \param chan Channel to handle.
@@ -581,6 +635,16 @@ struct ast_channel *stasis_app_bridge_moh_channel(
 int stasis_app_bridge_moh_stop(
 	struct ast_bridge *bridge);
 
+/*!
+ * \brief Result codes used when adding/removing channels to/from bridges.
+ */
+enum stasis_app_control_channel_result {
+	/*! The channel is okay to be added/removed */
+	STASIS_APP_CHANNEL_OKAY = 0,
+	/*! The channel is currently recording */
+	STASIS_APP_CHANNEL_RECORDING
+};
+
 /*!
  * \brief Add a channel to the bridge.
  *
diff --git a/include/asterisk/stasis_app_impl.h b/include/asterisk/stasis_app_impl.h
index d4b467756427dd9da6a8dbe68860bb0c5814705e..a8c8c05866c1e42e27f9eab83470c48849d73722 100644
--- a/include/asterisk/stasis_app_impl.h
+++ b/include/asterisk/stasis_app_impl.h
@@ -49,7 +49,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
 	char *argv[]);
 
 /*! Callback type for stasis app commands */
-typedef void *(*stasis_app_command_cb)(struct stasis_app_control *control,
+typedef int (*stasis_app_command_cb)(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data);
 
 /*!
@@ -63,10 +63,11 @@ typedef void *(*stasis_app_command_cb)(struct stasis_app_control *control,
  * \param control Control object for the channel to send the command to.
  * \param command Command function to execute.
  * \param data Optional data to pass along with the control function.
- * \return Return value from \a command.
- * \return \c NULL on error.
+ *
+ * \return zero on success.
+ * \return error code otherwise.
  */
-void *stasis_app_send_command(struct stasis_app_control *control,
+int stasis_app_send_command(struct stasis_app_control *control,
 	stasis_app_command_cb command, void *data);
 
 /*!
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index e09bea6b56acfd74f0f254792fa48789e5a42f4b..c074718160703fad4889525d92d51a83cba8d9c5 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -172,6 +172,22 @@ static struct control_list *control_list_create(struct ast_ari_response *respons
 	return list;
 }
 
+static int check_add_remove_channel(struct ast_ari_response *response,
+				    struct stasis_app_control *control,
+				    enum stasis_app_control_channel_result result)
+{
+	switch (result) {
+	case STASIS_APP_CHANNEL_RECORDING :
+		ast_ari_response_error(
+			response, 409, "Conflict", "Channel %s currently recording",
+			stasis_app_control_get_channel_id(control));
+		return -1;
+	case STASIS_APP_CHANNEL_OKAY:
+		return 0;
+	}
+	return 0;
+}
+
 void ast_ari_bridges_add_channel(struct ast_variable *headers,
 	struct ast_ari_bridges_add_channel_args *args,
 	struct ast_ari_response *response)
@@ -179,6 +195,7 @@ void ast_ari_bridges_add_channel(struct ast_variable *headers,
 	RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
 	RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
 	size_t i;
+	int has_error = 0;
 
 	if (!bridge) {
 		/* Response filled in by find_bridge() */
@@ -202,10 +219,16 @@ void ast_ari_bridges_add_channel(struct ast_variable *headers,
 	}
 
 	for (i = 0; i < list->count; ++i) {
-		stasis_app_control_add_channel_to_bridge(list->controls[i], bridge);
+		if ((has_error = check_add_remove_channel(response, list->controls[i],
+			     stasis_app_control_add_channel_to_bridge(
+				    list->controls[i], bridge)))) {
+			break;
+		}
 	}
 
-	ast_ari_response_no_content(response);
+	if (!has_error) {
+		ast_ari_response_no_content(response);
+	}
 }
 
 void ast_ari_bridges_remove_channel(struct ast_variable *headers,
diff --git a/res/res_ari_bridges.c b/res/res_ari_bridges.c
index f72a728053a96ed462f56152cc119ca92c4db28c..a68fbf6ac0194dd592d93bcce2e9b7e4a2665b67 100644
--- a/res/res_ari_bridges.c
+++ b/res/res_ari_bridges.c
@@ -438,7 +438,7 @@ static void ast_ari_bridges_add_channel_cb(
 	case 501: /* Not Implemented */
 	case 400: /* Channel not found */
 	case 404: /* Bridge not found */
-	case 409: /* Bridge not in Stasis application */
+	case 409: /* Bridge not in Stasis application; Channel currently recording */
 	case 422: /* Channel not in Stasis application */
 		is_valid = 1;
 		break;
diff --git a/res/res_stasis_answer.c b/res/res_stasis_answer.c
index 257afb5c6ba0809119439e786899dc46ce816a08..bc7d2fba314c661721f9234068350da8af59258a 100644
--- a/res/res_stasis_answer.c
+++ b/res/res_stasis_answer.c
@@ -35,28 +35,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app_impl.h"
 
-static int OK = 0;
-static int FAIL = -1;
-
-static void *app_control_answer(struct stasis_app_control *control,
+static int app_control_answer(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	const int delay = 0;
 	ast_debug(3, "%s: Answering",
 		stasis_app_control_get_channel_id(control));
-	return __ast_answer(chan, delay) == 0 ? &OK : &FAIL;
+	return __ast_answer(chan, delay);
 }
 
 int stasis_app_control_answer(struct stasis_app_control *control)
 {
-	int *retval;
+	int retval;
 
 	ast_debug(3, "%s: Sending answer command\n",
 		stasis_app_control_get_channel_id(control));
 
 	retval = stasis_app_send_command(control, app_control_answer, NULL);
 
-	if (retval == NULL || *retval != 0) {
+	if (retval != 0) {
 		ast_log(LOG_WARNING, "%s: Failed to answer channel",
 			stasis_app_control_get_channel_id(control));
 		return -1;
diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c
index ce29de12ce38ce3d842e4500fc8830f2a1de632a..f6cd5404e2dbcc90a4ad315bf1f7844796dee6d8 100644
--- a/res/res_stasis_playback.c
+++ b/res/res_stasis_playback.c
@@ -389,7 +389,7 @@ static void remove_from_playbacks(struct stasis_app_playback *playback)
 		OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
 }
 
-static void *play_uri(struct stasis_app_control *control,
+static int play_uri(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct stasis_app_playback *, playback, NULL,
@@ -400,7 +400,7 @@ static void *play_uri(struct stasis_app_control *control,
 	playback = data;
 
 	if (!control) {
-		return NULL;
+		return -1;
 	}
 
 	bridge = stasis_app_get_bridge(control);
@@ -435,7 +435,7 @@ static void *play_uri(struct stasis_app_control *control,
 		play_on_channel(playback, chan);
 	}
 
-	return NULL;
+	return 0;
 }
 
 static void set_target_uri(
diff --git a/res/res_stasis_recording.c b/res/res_stasis_recording.c
index ecf0cfa49f51f39bfa8db6053b5e51b9a909ff1e..f0fa1e5f6fdb71c7e1817ec535f85ab1e9bf6ef2 100644
--- a/res/res_stasis_recording.c
+++ b/res/res_stasis_recording.c
@@ -244,20 +244,43 @@ static void recording_publish(struct stasis_app_recording *recording, const char
 	stasis_app_control_publish(recording->control, message);
 }
 
-static void recording_fail(struct stasis_app_recording *recording, const char *cause)
+
+static void recording_set_state(struct stasis_app_recording *recording,
+				enum stasis_app_recording_state state,
+				const char *cause)
 {
 	SCOPED_AO2LOCK(lock, recording);
-	recording->state = STASIS_APP_RECORDING_STATE_FAILED;
+	recording->state = state;
 	recording_publish(recording, cause);
 }
 
+static enum stasis_app_control_channel_result check_rule_recording(
+	const struct stasis_app_control *control)
+{
+	return STASIS_APP_CHANNEL_RECORDING;
+}
+
+struct stasis_app_control_rule rule_recording = {
+	.check_rule = check_rule_recording
+};
+
+static void recording_fail(struct stasis_app_control *control,
+			   struct stasis_app_recording *recording,
+			   const char *cause)
+{
+	stasis_app_control_unregister_add_rule(control, &rule_recording);
+
+	recording_set_state(
+		recording, STASIS_APP_RECORDING_STATE_FAILED, cause);
+}
+
 static void recording_cleanup(struct stasis_app_recording *recording)
 {
 	ao2_unlink_flags(recordings, recording,
 		OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
 }
 
-static void *record_file(struct stasis_app_control *control,
+static int record_file(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct stasis_app_recording *, recording,
@@ -271,8 +294,8 @@ static void *record_file(struct stasis_app_control *control,
 
 	if (stasis_app_get_bridge(control)) {
 		ast_log(LOG_ERROR, "Cannot record channel while in bridge\n");
-		recording_fail(recording, "Cannot record channel while in bridge");
-		return NULL;
+		recording_fail(control, recording, "Cannot record channel while in bridge");
+		return -1;
 	}
 
 	switch (recording->options->terminate_on) {
@@ -293,15 +316,12 @@ static void *record_file(struct stasis_app_control *control,
 	if (res != 0) {
 		ast_debug(3, "%s: Failed to answer\n",
 			ast_channel_uniqueid(chan));
-		recording_fail(recording, "Failed to answer channel");
-		return NULL;
+		recording_fail(control, recording, "Failed to answer channel");
+		return -1;
 	}
 
-	ao2_lock(recording);
-	recording->state = STASIS_APP_RECORDING_STATE_RECORDING;
-	recording_publish(recording, NULL);
-	ao2_unlock(recording);
-
+	recording_set_state(
+		recording, STASIS_APP_RECORDING_STATE_RECORDING, NULL);
 	ast_play_and_record_full(chan,
 		NULL, /* playfile */
 		recording->absolute_name,
@@ -320,12 +340,12 @@ static void *record_file(struct stasis_app_control *control,
 
 	ast_debug(3, "%s: Recording complete\n", ast_channel_uniqueid(chan));
 
-	ao2_lock(recording);
-	recording->state = STASIS_APP_RECORDING_STATE_COMPLETE;
-	recording_publish(recording, NULL);
-	ao2_unlock(recording);
+	recording_set_state(
+		recording, STASIS_APP_RECORDING_STATE_COMPLETE, NULL);
+
+	stasis_app_control_unregister_add_rule(control, &rule_recording);
 
-	return NULL;
+	return 0;
 }
 
 static void recording_dtor(void *obj)
@@ -412,6 +432,8 @@ struct stasis_app_recording *stasis_app_control_record(
 		ao2_link(recordings, recording);
 	}
 
+	stasis_app_control_register_add_rule(control, &rule_recording);
+
 	/* A ref is kept in the recordings container; no need to bump */
 	stasis_app_send_command_async(control, record_file, recording);
 
diff --git a/res/stasis/command.c b/res/stasis/command.c
index f1f7f8f3b4903d15f1910d4a95979e3284e540a4..a9e53af12581102c0fd3f76924a99245eb2d5366 100644
--- a/res/stasis/command.c
+++ b/res/stasis/command.c
@@ -37,7 +37,7 @@ struct stasis_app_command {
 	ast_cond_t condition;
 	stasis_app_command_cb callback;
 	void *data;
-	void *retval;
+	int retval;
 	int is_done:1;
 };
 
@@ -67,7 +67,7 @@ struct stasis_app_command *command_create(
 	return command;
 }
 
-static void command_complete(struct stasis_app_command *command, void *retval)
+void command_complete(struct stasis_app_command *command, int retval)
 {
 	SCOPED_MUTEX(lock, &command->lock);
 
@@ -76,7 +76,7 @@ static void command_complete(struct stasis_app_command *command, void *retval)
 	ast_cond_signal(&command->condition);
 }
 
-void *command_join(struct stasis_app_command *command)
+int command_join(struct stasis_app_command *command)
 {
 	SCOPED_MUTEX(lock, &command->lock);
 	while (!command->is_done) {
@@ -89,7 +89,7 @@ void *command_join(struct stasis_app_command *command)
 void command_invoke(struct stasis_app_command *command,
 	struct stasis_app_control *control, struct ast_channel *chan)
 {
-	void *retval = command->callback(control, chan, command->data);
+	int retval = command->callback(control, chan, command->data);
 	command_complete(command, retval);
 }
 
diff --git a/res/stasis/command.h b/res/stasis/command.h
index 21f4df0c0ab4e40828400356e3870d065c7bfe99..a99d40d0a6e071c66444d9f18de85cdc5fce228c 100644
--- a/res/stasis/command.h
+++ b/res/stasis/command.h
@@ -34,9 +34,11 @@ struct stasis_app_command;
 struct stasis_app_command *command_create(
 	stasis_app_command_cb callback, void *data);
 
+void command_complete(struct stasis_app_command *command, int retval);
+
 void command_invoke(struct stasis_app_command *command,
 	struct stasis_app_control *control, struct ast_channel *chan);
 
-void *command_join(struct stasis_app_command *command);
+int command_join(struct stasis_app_command *command);
 
 #endif /* _ASTERISK_RES_STASIS_CONTROL_H */
diff --git a/res/stasis/control.c b/res/stasis/control.c
index 14c9f57f5bb5c49e04ec1b6c6649424a1d1e95f7..6b092918edd28d965d653a4529bd3c164775ecfb 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -40,6 +40,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/musiconhold.h"
 #include "asterisk/app.h"
 
+AST_LIST_HEAD(app_control_rules, stasis_app_control_rule);
+
 struct stasis_app_control {
 	ast_cond_t wait_cond;
 	/*! Queue of commands to dispatch on the channel */
@@ -58,6 +60,14 @@ struct stasis_app_control {
 	 * Holding place for channel's PBX while imparted to a bridge.
 	 */
 	struct ast_pbx *pbx;
+	/*!
+	 * A list of rules to check before adding a channel to a bridge.
+	 */
+	struct app_control_rules add_rules;
+	/*!
+	 * A list of rules to check before removing a channel from a bridge.
+	 */
+	struct app_control_rules remove_rules;
 	/*!
 	 * Silence generator, when silence is being generated.
 	 */
@@ -72,6 +82,9 @@ static void control_dtor(void *obj)
 {
 	struct stasis_app_control *control = obj;
 
+	AST_LIST_HEAD_DESTROY(&control->add_rules);
+	AST_LIST_HEAD_DESTROY(&control->remove_rules);
+
 	/* We may have a lingering silence generator; free it */
 	ast_channel_stop_silence_generator(control->channel, control->silgen);
 	control->silgen = NULL;
@@ -106,22 +119,121 @@ struct stasis_app_control *control_create(struct ast_channel *channel)
 
 	control->channel = channel;
 
+	AST_LIST_HEAD_INIT(&control->add_rules);
+	AST_LIST_HEAD_INIT(&control->remove_rules);
+
 	ao2_ref(control, +1);
 	return control;
 }
 
-static void *noop_cb(struct stasis_app_control *control,
+static void app_control_register_rule(
+	const struct stasis_app_control *control,
+	struct app_control_rules *list, struct stasis_app_control_rule *obj)
+{
+	SCOPED_AO2LOCK(lock, control->command_queue);
+	AST_LIST_INSERT_TAIL(list, obj, next);
+}
+
+static void app_control_unregister_rule(
+	const struct stasis_app_control *control,
+	struct app_control_rules *list, struct stasis_app_control_rule *obj)
+{
+	struct stasis_app_control_rule *rule;
+	SCOPED_AO2LOCK(lock, control->command_queue);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(list, rule, next) {
+		if (rule == obj) {
+			AST_RWLIST_REMOVE_CURRENT(next);
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+}
+
+/*!
+ * \internal
+ * \brief Checks to make sure each rule in the given list passes.
+ *
+ * \details Loops over a list of rules checking for rejections or failures.
+ *          If one rule fails its resulting error code is returned.
+ *
+ * \note Command queue should be locked before calling this function.
+ *
+ * \param control The stasis application control
+ * \param list The list of rules to check
+ *
+ * \retval 0 if all rules pass
+ * \retval non-zero error code if a rule fails
+ */
+static enum stasis_app_control_channel_result app_control_check_rules(
+	const struct stasis_app_control *control,
+	struct app_control_rules *list)
+{
+	int res = 0;
+	struct stasis_app_control_rule *rule;
+	AST_LIST_TRAVERSE(list, rule, next) {
+		if ((res = rule->check_rule(control))) {
+			return res;
+		}
+	}
+	return res;
+}
+
+void stasis_app_control_register_add_rule(
+	struct stasis_app_control *control,
+	struct stasis_app_control_rule *rule)
+{
+	return app_control_register_rule(control, &control->add_rules, rule);
+}
+
+void stasis_app_control_unregister_add_rule(
+	struct stasis_app_control *control,
+	struct stasis_app_control_rule *rule)
+{
+	app_control_unregister_rule(control, &control->add_rules, rule);
+}
+
+void stasis_app_control_register_remove_rule(
+	struct stasis_app_control *control,
+	struct stasis_app_control_rule *rule)
+{
+	return app_control_register_rule(control, &control->remove_rules, rule);
+}
+
+void stasis_app_control_unregister_remove_rule(
+	struct stasis_app_control *control,
+	struct stasis_app_control_rule *rule)
+{
+	app_control_unregister_rule(control, &control->remove_rules, rule);
+}
+
+static int app_control_can_add_channel_to_bridge(
+	struct stasis_app_control *control)
+{
+	return app_control_check_rules(control, &control->add_rules);
+}
+
+static int app_control_can_remove_channel_from_bridge(
+	struct stasis_app_control *control)
+{
+	return app_control_check_rules(control, &control->remove_rules);
+}
+
+static int noop_cb(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
-	return NULL;
+	return 0;
 }
 
+/*! Callback type to see if the command can execute
+    note: command_queue is locked during callback */
+typedef int (*app_command_can_exec_cb)(struct stasis_app_control *control);
 
-static struct stasis_app_command *exec_command(
+static struct stasis_app_command *exec_command_on_condition(
 	struct stasis_app_control *control, stasis_app_command_cb command_fn,
-	void *data)
+	void *data, app_command_can_exec_cb can_exec_fn)
 {
-	RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
+	int retval;
+	struct stasis_app_command *command;
 
 	command_fn = command_fn ? : noop_cb;
 
@@ -131,24 +243,36 @@ static struct stasis_app_command *exec_command(
 	}
 
 	ao2_lock(control->command_queue);
+	if (can_exec_fn && (retval = can_exec_fn(control))) {
+		ao2_unlock(control->command_queue);
+		command_complete(command, retval);
+		return command;
+	}
+
 	ao2_link_flags(control->command_queue, command, OBJ_NOLOCK);
 	ast_cond_signal(&control->wait_cond);
 	ao2_unlock(control->command_queue);
 
-	ao2_ref(command, +1);
 	return command;
 }
 
+static struct stasis_app_command *exec_command(
+	struct stasis_app_control *control, stasis_app_command_cb command_fn,
+	void *data)
+{
+	return exec_command_on_condition(control, command_fn, data, NULL);
+}
+
 struct stasis_app_control_dial_data {
 	char endpoint[AST_CHANNEL_NAME];
 	int timeout;
 };
 
-static void *app_control_add_channel_to_bridge(
+static int app_control_add_channel_to_bridge(
         struct stasis_app_control *control,
         struct ast_channel *chan, void *data);
 
-static void *app_control_dial(struct stasis_app_control *control,
+static int app_control_dial(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct ast_dial *, dial, ast_dial_create(), ast_dial_destroy);
@@ -160,30 +284,30 @@ static void *app_control_dial(struct stasis_app_control *control,
 
 	tech = dial_data->endpoint;
 	if (!(resource = strchr(tech, '/'))) {
-		return NULL;
+		return -1;
 	}
 	*resource++ = '\0';
 
 	if (!dial) {
 		ast_log(LOG_ERROR, "Failed to create dialing structure.\n");
-		return NULL;
+		return -1;
 	}
 
 	if (ast_dial_append(dial, tech, resource) < 0) {
 		ast_log(LOG_ERROR, "Failed to add %s/%s to dialing structure.\n", tech, resource);
-		return NULL;
+		return -1;
 	}
 
 	ast_dial_set_global_timeout(dial, dial_data->timeout);
 
 	res = ast_dial_run(dial, NULL, 0);
 	if (res != AST_DIAL_RESULT_ANSWERED || !(new_chan = ast_dial_answered_steal(dial))) {
-		return NULL;
+		return -1;
 	}
 
 	if (!(bridge = ast_bridge_basic_new())) {
 		ast_log(LOG_ERROR, "Failed to create basic bridge.\n");
-		return NULL;
+		return -1;
 	}
 
 	if (ast_bridge_impart(bridge, new_chan, NULL, NULL,
@@ -193,7 +317,7 @@ static void *app_control_dial(struct stasis_app_control *control,
 		app_control_add_channel_to_bridge(control, chan, bridge);
 	}
 
-	return NULL;
+	return 0;
 }
 
 int stasis_app_control_dial(struct stasis_app_control *control, const char *endpoint, const char *exten, const char *context,
@@ -248,7 +372,7 @@ struct stasis_app_control_continue_data {
 	int priority;
 };
 
-static void *app_control_continue(struct stasis_app_control *control,
+static int app_control_continue(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct stasis_app_control_continue_data *, continue_data, data, ast_free);
@@ -266,7 +390,7 @@ static void *app_control_continue(struct stasis_app_control *control,
 
 	control->is_done = 1;
 
-	return NULL;
+	return 0;
 }
 
 int stasis_app_control_continue(struct stasis_app_control *control, const char *context, const char *extension, int priority)
@@ -297,7 +421,7 @@ struct stasis_app_control_dtmf_data {
 	char dtmf[];
 };
 
-static void *app_control_dtmf(struct stasis_app_control *control,
+static int app_control_dtmf(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct stasis_app_control_dtmf_data *, dtmf_data, data, ast_free);
@@ -312,7 +436,7 @@ static void *app_control_dtmf(struct stasis_app_control *control,
 		ast_safe_sleep(chan, dtmf_data->after);
 	}
 
-	return NULL;
+	return 0;
 }
 
 int stasis_app_control_dtmf(struct stasis_app_control *control, const char *dtmf, int before, int between, unsigned int duration, int after)
@@ -334,12 +458,12 @@ int stasis_app_control_dtmf(struct stasis_app_control *control, const char *dtmf
 	return 0;
 }
 
-static void *app_control_ring(struct stasis_app_control *control,
+static int app_control_ring(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	ast_indicate(control->channel, AST_CONTROL_RINGING);
 
-	return NULL;
+	return 0;
 }
 
 int stasis_app_control_ring(struct stasis_app_control *control)
@@ -349,12 +473,12 @@ int stasis_app_control_ring(struct stasis_app_control *control)
 	return 0;
 }
 
-static void *app_control_ring_stop(struct stasis_app_control *control,
+static int app_control_ring_stop(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	ast_indicate(control->channel, -1);
 
-	return NULL;
+	return 0;
 }
 
 int stasis_app_control_ring_stop(struct stasis_app_control *control)
@@ -369,7 +493,7 @@ struct stasis_app_control_mute_data {
 	unsigned int direction;
 };
 
-static void *app_control_mute(struct stasis_app_control *control,
+static int app_control_mute(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct stasis_app_control_mute_data *, mute_data, data, ast_free);
@@ -377,7 +501,7 @@ static void *app_control_mute(struct stasis_app_control *control,
 
 	ast_channel_suppress(control->channel, mute_data->direction, mute_data->frametype);
 
-	return NULL;
+	return 0;
 }
 
 int stasis_app_control_mute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
@@ -396,7 +520,7 @@ int stasis_app_control_mute(struct stasis_app_control *control, unsigned int dir
 	return 0;
 }
 
-static void *app_control_unmute(struct stasis_app_control *control,
+static int app_control_unmute(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct stasis_app_control_mute_data *, mute_data, data, ast_free);
@@ -404,7 +528,7 @@ static void *app_control_unmute(struct stasis_app_control *control,
 
 	ast_channel_unsuppress(control->channel, mute_data->direction, mute_data->frametype);
 
-	return NULL;
+	return 0;
 }
 
 int stasis_app_control_unmute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
@@ -456,12 +580,12 @@ int stasis_app_control_set_channel_var(struct stasis_app_control *control, const
 	return pbx_builtin_setvar_helper(control->channel, variable, value);
 }
 
-static void *app_control_hold(struct stasis_app_control *control,
+static int app_control_hold(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	ast_indicate(control->channel, AST_CONTROL_HOLD);
 
-	return NULL;
+	return 0;
 }
 
 void stasis_app_control_hold(struct stasis_app_control *control)
@@ -469,12 +593,12 @@ void stasis_app_control_hold(struct stasis_app_control *control)
 	stasis_app_send_command_async(control, app_control_hold, NULL);
 }
 
-static void *app_control_unhold(struct stasis_app_control *control,
+static int app_control_unhold(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	ast_indicate(control->channel, AST_CONTROL_UNHOLD);
 
-	return NULL;
+	return 0;
 }
 
 void stasis_app_control_unhold(struct stasis_app_control *control)
@@ -482,7 +606,7 @@ void stasis_app_control_unhold(struct stasis_app_control *control)
 	stasis_app_send_command_async(control, app_control_unhold, NULL);
 }
 
-static void *app_control_moh_start(struct stasis_app_control *control,
+static int app_control_moh_start(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	char *moh_class = data;
@@ -490,7 +614,7 @@ static void *app_control_moh_start(struct stasis_app_control *control,
 	ast_moh_start(chan, moh_class, NULL);
 
 	ast_free(moh_class);
-	return NULL;
+	return 0;
 }
 
 void stasis_app_control_moh_start(struct stasis_app_control *control, const char *moh_class)
@@ -504,11 +628,11 @@ void stasis_app_control_moh_start(struct stasis_app_control *control, const char
 	stasis_app_send_command_async(control, app_control_moh_start, data);
 }
 
-static void *app_control_moh_stop(struct stasis_app_control *control,
+static int app_control_moh_stop(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	ast_moh_stop(chan);
-	return NULL;
+	return 0;
 }
 
 void stasis_app_control_moh_stop(struct stasis_app_control *control)
@@ -516,7 +640,7 @@ void stasis_app_control_moh_stop(struct stasis_app_control *control)
 	stasis_app_send_command_async(control, app_control_moh_stop, NULL);
 }
 
-static void *app_control_silence_start(struct stasis_app_control *control,
+static int app_control_silence_start(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	if (control->silgen) {
@@ -538,7 +662,7 @@ static void *app_control_silence_start(struct stasis_app_control *control,
 			stasis_app_control_get_channel_id(control));
 	}
 
-	return NULL;
+	return 0;
 }
 
 void stasis_app_control_silence_start(struct stasis_app_control *control)
@@ -546,7 +670,7 @@ void stasis_app_control_silence_start(struct stasis_app_control *control)
 	stasis_app_send_command_async(control, app_control_silence_start, NULL);
 }
 
-static void *app_control_silence_stop(struct stasis_app_control *control,
+static int app_control_silence_stop(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	if (control->silgen) {
@@ -557,7 +681,7 @@ static void *app_control_silence_stop(struct stasis_app_control *control,
 		control->silgen = NULL;
 	}
 
-	return NULL;
+	return 0;
 }
 
 void stasis_app_control_silence_stop(struct stasis_app_control *control)
@@ -584,23 +708,31 @@ struct ast_channel_snapshot *stasis_app_control_get_snapshot(
 	return snapshot;
 }
 
-void *stasis_app_send_command(struct stasis_app_control *control,
-	stasis_app_command_cb command_fn, void *data)
+static int app_send_command_on_condition(struct stasis_app_control *control,
+					 stasis_app_command_cb command_fn, void *data,
+					 app_command_can_exec_cb can_exec_fn)
 {
 	RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
 
 	if (control == NULL) {
-		return NULL;
+		return -1;
 	}
 
-	command = exec_command(control, command_fn, data);
+	command = exec_command_on_condition(
+		control, command_fn, data, can_exec_fn);
 	if (!command) {
-		return NULL;
+		return -1;
 	}
 
 	return command_join(command);
 }
 
+int stasis_app_send_command(struct stasis_app_control *control,
+	stasis_app_command_cb command_fn, void *data)
+{
+	return app_send_command_on_condition(control, command_fn, data, NULL);
+}
+
 int stasis_app_send_command_async(struct stasis_app_control *control,
 	stasis_app_command_cb command_fn, void *data)
 {
@@ -628,7 +760,7 @@ struct ast_bridge *stasis_app_get_bridge(struct stasis_app_control *control)
 	}
 }
 
-static void *bridge_channel_depart(struct stasis_app_control *control,
+static int bridge_channel_depart(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	RAII_VAR(struct ast_bridge_channel *, bridge_channel, data, ao2_cleanup);
@@ -639,7 +771,7 @@ static void *bridge_channel_depart(struct stasis_app_control *control,
 		if (bridge_channel != ast_channel_internal_bridge_channel(chan)) {
 			ast_debug(3, "%s: Channel is no longer in departable state\n",
 				ast_channel_uniqueid(chan));
-			return NULL;
+			return -1;
 		}
 	}
 
@@ -648,7 +780,7 @@ static void *bridge_channel_depart(struct stasis_app_control *control,
 
 	ast_bridge_depart(chan);
 
-	return NULL;
+	return 0;
 }
 
 static void bridge_after_cb(struct ast_channel *chan, void *data)
@@ -691,10 +823,7 @@ static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
 		ast_bridge_after_cb_reason_string(reason));
 }
 
-static int OK = 0;
-static int FAIL = -1;
-
-static void *app_control_add_channel_to_bridge(
+static int app_control_add_channel_to_bridge(
 	struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
@@ -702,7 +831,7 @@ static void *app_control_add_channel_to_bridge(
 	int res;
 
 	if (!control || !bridge) {
-		return NULL;
+		return -1;
 	}
 
 	ast_debug(3, "%s: Adding to bridge %s\n",
@@ -726,7 +855,7 @@ static void *app_control_add_channel_to_bridge(
 		bridge_after_cb_failed, control);
 	if (res != 0) {
 		ast_log(LOG_ERROR, "Error setting after-bridge callback\n");
-		return &FAIL;
+		return -1;
 	}
 
 	{
@@ -752,34 +881,34 @@ static void *app_control_add_channel_to_bridge(
 			ast_log(LOG_ERROR, "Error adding channel to bridge\n");
 			ast_channel_pbx_set(chan, control->pbx);
 			control->pbx = NULL;
-			return &FAIL;
+			return -1;
 		}
 
 		ast_assert(stasis_app_get_bridge(control) == NULL);
 		control->bridge = bridge;
 	}
-	return &OK;
+	return 0;
 }
 
 int stasis_app_control_add_channel_to_bridge(
 	struct stasis_app_control *control, struct ast_bridge *bridge)
 {
-	int *res;
 	ast_debug(3, "%s: Sending channel add_to_bridge command\n",
 			stasis_app_control_get_channel_id(control));
-	res = stasis_app_send_command(control,
-		app_control_add_channel_to_bridge, bridge);
-	return *res;
+
+	return app_send_command_on_condition(
+		control, app_control_add_channel_to_bridge, bridge,
+		app_control_can_add_channel_to_bridge);
 }
 
-static void *app_control_remove_channel_from_bridge(
+static int app_control_remove_channel_from_bridge(
 	struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
 	struct ast_bridge *bridge = data;
 
 	if (!control) {
-		return &FAIL;
+		return -1;
 	}
 
 	/* We should only depart from our own bridge */
@@ -791,22 +920,21 @@ static void *app_control_remove_channel_from_bridge(
 		ast_log(LOG_WARNING, "%s: Not in bridge %s; not removing\n",
 			stasis_app_control_get_channel_id(control),
 			bridge->uniqueid);
-		return &FAIL;
+		return -1;
 	}
 
 	ast_bridge_depart(chan);
-	return &OK;
+	return 0;
 }
 
 int stasis_app_control_remove_channel_from_bridge(
 	struct stasis_app_control *control, struct ast_bridge *bridge)
 {
-	int *res;
 	ast_debug(3, "%s: Sending channel remove_from_bridge command\n",
 			stasis_app_control_get_channel_id(control));
-	res = stasis_app_send_command(control,
-		app_control_remove_channel_from_bridge, bridge);
-	return *res;
+	return app_send_command_on_condition(
+		control, app_control_remove_channel_from_bridge, bridge,
+		app_control_can_remove_channel_from_bridge);
 }
 
 const char *stasis_app_control_get_channel_id(
diff --git a/rest-api/api-docs/bridges.json b/rest-api/api-docs/bridges.json
index 2339a96711d364c82ed31ee08630990be7efa218..d7f63ad144bad912f930b211af518e6530d088ef 100644
--- a/rest-api/api-docs/bridges.json
+++ b/rest-api/api-docs/bridges.json
@@ -140,7 +140,7 @@
 						},
 						{
 							"code": 409,
-							"reason": "Bridge not in Stasis application"
+							"reason": "Bridge not in Stasis application; Channel currently recording"
 						},
 						{
 							"code": 422,