diff --git a/include/asterisk/json.h b/include/asterisk/json.h
index 8cb74a4596dc52bcb693f017f32b602675da5352..28ebfbd51be1ec2fa27cbe63fd6ac46c00e24a27 100644
--- a/include/asterisk/json.h
+++ b/include/asterisk/json.h
@@ -1010,13 +1010,24 @@ struct ast_party_id;
  */
 struct ast_json *ast_json_party_id(struct ast_party_id *party);
 
+enum ast_json_to_ast_vars_code {
+	/*! \brief Conversion successful */
+	AST_JSON_TO_AST_VARS_CODE_SUCCESS,
+	/*!
+	 * \brief Conversion failed because invalid value type supplied.
+	 * \note Only string values allowed.
+	 */
+	AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE,
+	/*! \brief Conversion failed because of allocation failure. (Out Of Memory) */
+	AST_JSON_TO_AST_VARS_CODE_OOM,
+};
+
 /*!
  * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
  * \since 12.5.0
  *
  * \param json_variables The JSON blob containing the variable
  * \param variables An out reference to the variables to populate.
- *        The pointer to the variables should be NULL when calling this.
  *
  * \code
  * struct ast_json *json_variables = ast_json_pack("[ { s: s } ]", "foo", "bar");
@@ -1026,10 +1037,9 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party);
  * res = ast_json_to_ast_variables(json_variables, &variables);
  * \endcode
  *
- * \retval 0 success
- * \retval -1 error
+ * \return Conversion enum ast_json_to_ast_vars_code status
  */
-int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables);
+enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables);
 
 /*!@}*/
 
diff --git a/main/json.c b/main/json.c
index 88e8077729890ad6c1bc2dcd25cca94e262bf495..35e6f16cef890c563927fb06653911e8626298fc 100644
--- a/main/json.c
+++ b/main/json.c
@@ -882,32 +882,47 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party)
 	return ast_json_ref(json_party_id);
 }
 
-int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
+enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
 {
 	struct ast_json_iter *it_json_var;
 
 	*variables = NULL;
 
 	for (it_json_var = ast_json_object_iter(json_variables); it_json_var;
-		 it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
+		it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
 		struct ast_variable *new_var;
 		const char *key = ast_json_object_iter_key(it_json_var);
+		const char *value;
+		struct ast_json *json_value;
 
 		if (ast_strlen_zero(key)) {
 			continue;
 		}
 
-		new_var = ast_variable_new(key,
-		                           ast_json_string_get(ast_json_object_iter_value(it_json_var)),
-		                           "");
+		json_value = ast_json_object_iter_value(it_json_var);
+		if (ast_json_typeof(json_value) != AST_JSON_STRING) {
+			/* Error: Only strings allowed */
+			ast_variables_destroy(*variables);
+			*variables = NULL;
+			return AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE;
+		}
+		value = ast_json_string_get(json_value);
+		/* Should never be NULL.  Otherwise, how could it be a string type? */
+		ast_assert(value != NULL);
+		if (!value) {
+			/* To be safe. */
+			continue;
+		}
+		new_var = ast_variable_new(key, value, "");
 		if (!new_var) {
+			/* Error: OOM */
 			ast_variables_destroy(*variables);
 			*variables = NULL;
-			return -1;
+			return AST_JSON_TO_AST_VARS_CODE_OOM;
 		}
 
 		ast_variable_list_append(variables, new_var);
 	}
 
-	return 0;
+	return AST_JSON_TO_AST_VARS_CODE_SUCCESS;
 }
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index 693835129e528c934966ad158ae1c99ca5a30414..fb1aa039c674dc013ecdba787b1bdf7089fc2b60 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -1147,11 +1147,44 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
 	return;
 }
 
+/*!
+ * \internal
+ * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
+ * \since 13.3.0
+ *
+ * \param[out] response HTTP response if error
+ * \param json_variables The JSON blob containing the variable
+ * \param[out] variables An out reference to the variables to populate.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
+{
+	enum ast_json_to_ast_vars_code res;
+
+	res = ast_json_to_ast_variables(json_variables, variables);
+	switch (res) {
+	case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
+		return 0;
+	case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
+		ast_ari_response_error(response, 400, "Bad Request",
+			"Only string values in the 'variables' object allowed");
+		break;
+	case AST_JSON_TO_AST_VARS_CODE_OOM:
+		ast_ari_response_alloc_failed(response);
+		break;
+	}
+	ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
+
+	return -1;
+}
+
 void ast_ari_channels_originate_with_id(struct ast_variable *headers,
 	struct ast_ari_channels_originate_with_id_args *args,
 	struct ast_ari_response *response)
 {
-	RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+	struct ast_variable *variables = NULL;
 
 	/* Parse any query parameters out of the body parameter */
 	if (args->variables) {
@@ -1159,12 +1192,9 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
 
 		ast_ari_channels_originate_with_id_parse_body(args->variables, args);
 		json_variables = ast_json_object_get(args->variables, "variables");
-		if (json_variables) {
-			if (ast_json_to_ast_variables(json_variables, &variables)) {
-				ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
-				ast_ari_response_alloc_failed(response);
-				return;
-			}
+		if (json_variables
+			&& json_to_ast_variables(response, json_variables, &variables)) {
+			return;
 		}
 	}
 
@@ -1183,13 +1213,14 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
 		args->other_channel_id,
 		args->originator,
 		response);
+	ast_variables_destroy(variables);
 }
 
 void ast_ari_channels_originate(struct ast_variable *headers,
 	struct ast_ari_channels_originate_args *args,
 	struct ast_ari_response *response)
 {
-	RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+	struct ast_variable *variables = NULL;
 
 	/* Parse any query parameters out of the body parameter */
 	if (args->variables) {
@@ -1197,12 +1228,9 @@ void ast_ari_channels_originate(struct ast_variable *headers,
 
 		ast_ari_channels_originate_parse_body(args->variables, args);
 		json_variables = ast_json_object_get(args->variables, "variables");
-		if (json_variables) {
-			if (ast_json_to_ast_variables(json_variables, &variables)) {
-				ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
-				ast_ari_response_alloc_failed(response);
-				return;
-			}
+		if (json_variables
+			&& json_to_ast_variables(response, json_variables, &variables)) {
+			return;
 		}
 	}
 
@@ -1221,6 +1249,7 @@ void ast_ari_channels_originate(struct ast_variable *headers,
 		args->other_channel_id,
 		args->originator,
 		response);
+	ast_variables_destroy(variables);
 }
 
 void ast_ari_channels_get_channel_var(struct ast_variable *headers,
diff --git a/res/ari/resource_endpoints.c b/res/ari/resource_endpoints.c
index 4f91e781d4146a6995d8d1c3fd8338236a5d9898..f794969f2925ce1b572cd0e5fc0e7099c9046bfe 100644
--- a/res/ari/resource_endpoints.c
+++ b/res/ari/resource_endpoints.c
@@ -220,35 +220,66 @@ static void send_message(const char *to, const char *from, const char *body, str
 	response->response_text = "Accepted";
 }
 
+/*!
+ * \internal
+ * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
+ * \since 13.3.0
+ *
+ * \param[out] response HTTP response if error
+ * \param json_variables The JSON blob containing the variable
+ * \param[out] variables An out reference to the variables to populate.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
+{
+	enum ast_json_to_ast_vars_code res;
+
+	res = ast_json_to_ast_variables(json_variables, variables);
+	switch (res) {
+	case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
+		return 0;
+	case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
+		ast_ari_response_error(response, 400, "Bad Request",
+			"Only string values in the 'variables' object allowed");
+		break;
+	case AST_JSON_TO_AST_VARS_CODE_OOM:
+		ast_ari_response_alloc_failed(response);
+		break;
+	}
+	ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
+
+	return -1;
+}
+
 void ast_ari_endpoints_send_message(struct ast_variable *headers,
 	struct ast_ari_endpoints_send_message_args *args,
 	struct ast_ari_response *response)
 {
-	RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+	struct ast_variable *variables = NULL;
 
 	if (args->variables) {
 		struct ast_json *json_variables;
 
 		ast_ari_endpoints_send_message_parse_body(args->variables, args);
 		json_variables = ast_json_object_get(args->variables, "variables");
-		if (json_variables) {
-			if (ast_json_to_ast_variables(json_variables, &variables)) {
-				ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
-				ast_ari_response_alloc_failed(response);
-				return;
-			}
+		if (json_variables
+			&& json_to_ast_variables(response, json_variables, &variables)) {
+			return;
 		}
 	}
 
 	send_message(args->to, args->from, args->body, variables, response);
+	ast_variables_destroy(variables);
 }
 
 void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers,
 	struct ast_ari_endpoints_send_message_to_endpoint_args *args,
 	struct ast_ari_response *response)
 {
-	RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
-	RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup);
+	struct ast_variable *variables = NULL;
+	struct ast_endpoint_snapshot *snapshot;
 	char msg_to[128];
 	char *tech = ast_strdupa(args->tech);
 
@@ -259,23 +290,21 @@ void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers,
 			"Endpoint not found");
 		return;
 	}
+	ao2_ref(snapshot, -1);
 
 	if (args->variables) {
 		struct ast_json *json_variables;
 
 		ast_ari_endpoints_send_message_to_endpoint_parse_body(args->variables, args);
 		json_variables = ast_json_object_get(args->variables, "variables");
-
-		if (json_variables) {
-			if (ast_json_to_ast_variables(json_variables, &variables)) {
-				ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
-				ast_ari_response_alloc_failed(response);
-				return;
-			}
+		if (json_variables
+			&& json_to_ast_variables(response, json_variables, &variables)) {
+			return;
 		}
 	}
 
 	snprintf(msg_to, sizeof(msg_to), "%s:%s", ast_str_to_lower(tech), args->resource);
 
 	send_message(msg_to, args->from, args->body, variables, response);
+	ast_variables_destroy(variables);
 }
diff --git a/res/res_ari_endpoints.c b/res/res_ari_endpoints.c
index 3ebe668585979231fa75744d5fdacc62dd21aa63..8d8ed716c19b98ed8708a7570f87fbf6340a1dad 100644
--- a/res/res_ari_endpoints.c
+++ b/res/res_ari_endpoints.c
@@ -181,6 +181,7 @@ static void ast_ari_endpoints_send_message_cb(
 		break;
 	case 500: /* Internal Server Error */
 	case 501: /* Not Implemented */
+	case 400: /* Invalid parameters for sending a message. */
 	case 404: /* Endpoint not found */
 		is_valid = 1;
 		break;
diff --git a/rest-api/api-docs/endpoints.json b/rest-api/api-docs/endpoints.json
index e7b4ba73a19935e3e71954967dc0bb097c52428a..17b884784a78a6aadc3e1a3b64e11339754fd646 100644
--- a/rest-api/api-docs/endpoints.json
+++ b/rest-api/api-docs/endpoints.json
@@ -63,6 +63,10 @@
 						}
 					],
 					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Invalid parameters for sending a message."
+						},
 						{
 							"code": 404,
 							"reason": "Endpoint not found"