Index: asterisk-22.8.2/res/ari.make
===================================================================
--- asterisk-22.8.2.orig/res/ari.make
+++ asterisk-22.8.2/res/ari.make
@@ -28,3 +28,4 @@ $(call MOD_ADD_C,res_ari_device_states,a
 $(call MOD_ADD_C,res_ari_mailboxes,ari/resource_mailboxes.c)
 $(call MOD_ADD_C,res_ari_events,ari/resource_events.c)
 $(call MOD_ADD_C,res_ari_applications,ari/resource_applications.c)
+$(call MOD_ADD_C,res_ari_wazo,ari/resource_wazo.c)
Index: asterisk-22.8.2/res/ari/resource_wazo.c
===================================================================
--- /dev/null
+++ asterisk-22.8.2/res/ari/resource_wazo.c
@@ -0,0 +1,417 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2016-2024 The Wazo Authors  (see the AUTHORS file)
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief /api-docs/wazo.{format} implementation- Wazo resources
+ */
+
+#include "asterisk.h"
+#include "asterisk/app.h"
+#include "resource_wazo.h"
+
+#define MAX_FORMAT_LEN 80
+
+static int validate_greeting(const char *greeting);
+
+
+void ast_ari_wazo_delete_voicemail_message(struct ast_variable *headers,
+	struct ast_ari_wazo_delete_voicemail_message_args *args,
+	struct ast_ari_response *response)
+{
+	const char *mailbox;
+	const char *context;
+	const char *folder;
+	const char *message_id;
+
+	mailbox = ast_json_string_get(ast_json_object_get(args->body, "mailbox"));
+	context = ast_json_string_get(ast_json_object_get(args->body, "context"));
+	folder = ast_json_string_get(ast_json_object_get(args->body, "folder"));
+	message_id = ast_json_string_get(ast_json_object_get(args->body, "message_id"));
+	if (!mailbox || !context || !folder || !message_id) {
+		ast_ari_response_error(response, 400, "Bad request", "Missing or invalid fields in body");
+		return;
+	}
+
+	if (strcmp(folder, "Deleted") == 0) {
+		/* ast_vm_msg_remove doesn't handle this case properly */
+		ast_ari_response_error(response, 400, "Bad request",
+			"Deleting a message from the Deleted folder is not supported");
+		return;
+	}
+
+	if (ast_vm_msg_remove(mailbox, context, 1, folder, &message_id) == -1) {
+		ast_ari_response_error(response, 500, "Internal Server Error", "Error deleting message");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
+void ast_ari_wazo_move_voicemail_message(struct ast_variable *headers,
+	struct ast_ari_wazo_move_voicemail_message_args *args,
+	struct ast_ari_response *response)
+{
+	const char *mailbox;
+	const char *context;
+	const char *src_folder;
+	const char *dest_folder;
+	const char *message_id;
+
+	mailbox = ast_json_string_get(ast_json_object_get(args->body, "mailbox"));
+	context = ast_json_string_get(ast_json_object_get(args->body, "context"));
+	src_folder = ast_json_string_get(ast_json_object_get(args->body, "src_folder"));
+	dest_folder = ast_json_string_get(ast_json_object_get(args->body, "dest_folder"));
+	message_id = ast_json_string_get(ast_json_object_get(args->body, "message_id"));
+	if (!mailbox || !context || !src_folder || !dest_folder || !message_id) {
+		ast_ari_response_error(response, 400, "Bad request", "Missing or invalid fields in body");
+		return;
+	}
+
+	if (strcmp(src_folder, dest_folder) == 0) {
+		/* ast_vm_msg_move doesn't handle it this case properly */
+		ast_ari_response_no_content(response);
+		return;
+	}
+
+	if (ast_vm_msg_move(mailbox, context, 1, src_folder, &message_id, dest_folder) == -1) {
+		ast_ari_response_error(response, 500, "Internal Server Error", "Error moving message");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
+
+
+void ast_ari_wazo_get_voicemail_greeting(struct ast_variable *headers,
+	struct ast_ari_wazo_get_voicemail_greeting_args *args,
+	struct ast_ari_response *response)
+{
+
+	char *encoded_greeting = NULL;
+	char *raw_greeting = NULL;
+	char format[MAX_FORMAT_LEN];
+	int raw_length = 0;
+	int num_bytes_encoded = 0;
+	unsigned long encoded_length = 0;
+
+	const char *greeting = args->greeting;
+	const char *context = args->context;
+	const char *voicemail = args->voicemail;
+	const char *arg_format = args->format;
+
+	if (!greeting) {
+		ast_ari_response_error(response, 400, "Invalid argument", "No greeting specified");
+		return;
+	}
+
+	if (!validate_greeting(greeting)) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid greeting");
+		return;
+	}
+
+	if (!context) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid context");
+		return;
+	}
+
+	if (!voicemail) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid voicemail");
+		return;
+	}
+
+	if (!arg_format) {
+		strcpy(format, "wav");
+	} else if (ast_strlen_zero(arg_format) || strlen(arg_format) >= MAX_FORMAT_LEN - 1) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid format");
+		goto data_cleanup;
+	} else {
+		strcpy(format, arg_format);
+	}
+	raw_length = ast_vm_get_greeting(&raw_greeting, context, voicemail, greeting, format);
+
+	if (raw_length == -3) {
+		ast_ari_response_error(response, 500, "Internal Server Error", "Unable to allocate memory");
+		goto data_cleanup;
+	} else if (raw_length < 0) {
+		ast_ari_response_error(response, 404, "Error", "Unable to read greeting file");
+		goto data_cleanup;
+	}
+
+	if (!raw_greeting) {
+		ast_ari_response_error(response, 404, "Error", "Unable to read greeting file");
+		ast_log(LOG_ERROR, "Unable to read file");
+		return;
+	}
+
+   /* The ratio of output bytes to input bytes is 4:3
+    * +10 is only there to cover fringe cases and possible padding
+    * for more details, see: https://en.wikipedia.org/wiki/Base64.
+    * innermost '+ 1' is meant to simulate ceil()
+    */
+	encoded_length = (4 * ((raw_length / 3) + 1)) + 1; /* allows space for null character */
+
+	encoded_greeting = ast_calloc(encoded_length, sizeof(char));
+
+	if (!encoded_greeting) {
+		ast_ari_response_error(response, 500, "Internal Server Error", "Unable to allocate memory");
+		goto data_cleanup;
+	}
+
+	num_bytes_encoded = ast_base64encode(encoded_greeting, (const unsigned char*) raw_greeting, raw_length,
+	                                             encoded_length);
+
+	if (num_bytes_encoded <= 0) {
+		ast_ari_response_error(response, 400, "Encoding Error", "Unable to encode base64");
+		goto data_cleanup;
+	}
+
+	response->message = ast_json_object_create();
+	ast_json_object_set(response->message, "greeting_base64", ast_json_string_create(encoded_greeting));
+
+	ast_ari_response_ok(response, response->message);
+
+data_cleanup:
+	ast_free(raw_greeting);
+	ast_free(encoded_greeting);
+}
+
+void ast_ari_wazo_create_voicemail_greeting(struct ast_variable *headers,
+	struct ast_ari_wazo_create_voicemail_greeting_args *args,
+	struct ast_ari_response *response)
+{
+	const char *encoded_greeting = ast_json_string_get(ast_json_object_get(args->body, "greeting_base64"));
+	char format[MAX_FORMAT_LEN];
+	const char *greeting = args->greeting;
+	const char *context = args->context;
+	const char *arg_format = args->format;
+	const char *voicemail = args->voicemail;
+	unsigned char *decoded_greeting = NULL;
+	long decoded_length = 0;
+	int num_bytes_decoded = 0;
+	int result = 0;
+
+	if (!encoded_greeting) {
+		ast_ari_response_error(response, 400, "Invalid argument", "No encoded greeting specified");
+		return;
+	}
+
+	if (!greeting) {
+		ast_ari_response_error(response, 400, "Invalid argument", "No greeting specified");
+		return;
+	}
+
+	if (!validate_greeting(greeting)) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid greeting");
+		return;
+	}
+
+   /* The ratio of output bytes to input bytes is 4:3
+    * +10 is only there to cover fringe cases and possible padding
+    * for more details, see: https://en.wikipedia.org/wiki/Base64
+    */
+	decoded_length = (3 * strlen(encoded_greeting) / 4) + 10;
+
+	if (decoded_length == 0L) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid greeting size value");
+		return;
+	}
+
+	decoded_greeting = ast_calloc(decoded_length, sizeof(char));
+	if (!decoded_greeting) {
+		ast_ari_response_error(response, 500, "Internal Server Error", "Unable to allocate memory");
+		return;
+	}
+
+	num_bytes_decoded = ast_base64decode(decoded_greeting, encoded_greeting, decoded_length - 1);
+	if (num_bytes_decoded <= 0) {
+		ast_ari_response_error(response, 400, "Decoding Error", "Unable to decode base64");
+		goto data_cleanup;
+	}
+
+	if (!context) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid context");
+		goto data_cleanup;
+	}
+
+	if (!voicemail) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid voicemail");
+		goto data_cleanup;
+	}
+
+	if (!arg_format) {
+		strcpy(format, "wav");
+	} else if (ast_strlen_zero(arg_format) || strlen(arg_format) >= MAX_FORMAT_LEN - 1) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid format");
+		goto data_cleanup;
+	} else {
+		strcpy(format, arg_format);
+	}
+
+	result = ast_vm_create_greeting((const char*) decoded_greeting, num_bytes_decoded, context, voicemail, greeting, format);
+	if (result == -1) {
+		ast_ari_response_error(response, 400, "Unable to change greeting", "Unable to write file, invalid path");
+		goto data_cleanup;
+	} else if (result == -2) {
+		ast_ari_response_error(response, 404, "Unable to change greeting", "Unable to write file");
+		goto data_cleanup;
+	} else if (result == -4) {
+		ast_ari_response_error(response, 409, "Unable to change greeting", "Already exists");
+		goto data_cleanup;
+	}
+
+	ast_ari_response_no_content(response);
+
+data_cleanup:
+	ast_free(decoded_greeting);
+}
+
+void ast_ari_wazo_change_voicemail_greeting(struct ast_variable *headers,
+	struct ast_ari_wazo_change_voicemail_greeting_args *args,
+	struct ast_ari_response *response)
+{
+	char format[MAX_FORMAT_LEN];
+	const char *encoded_greeting = ast_json_string_get(ast_json_object_get(args->body, "greeting_base64"));
+	const char *greeting = args->greeting;
+	const char *context = args->context;
+	const char *voicemail = args->voicemail;
+	const char *arg_format = args->format;
+
+	unsigned char *decoded_greeting = NULL;
+	long decoded_length = 0;
+	int num_bytes_decoded = 0;
+	int result = 0;
+
+	if (!encoded_greeting) {
+		ast_ari_response_error(response, 400, "Invalid argument", "No encoded greeting specified");
+		return;
+	}
+
+	if (!greeting) {
+		ast_ari_response_error(response, 400, "Invalid argument", "No greeting specified");
+		return;
+	}
+
+	if (!validate_greeting(greeting)) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid greeting");
+		return;
+	}
+
+   /* The ratio of output bytes to input bytes is 4:3
+    * +10 is only there to cover fringe cases and possible padding
+    * for more details, see: https://en.wikipedia.org/wiki/Base64
+    */
+	decoded_length = (3 * strlen(encoded_greeting) / 4) + 10;
+
+	if (decoded_length == 0L) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid greeting size value");
+		return;
+	}
+
+	decoded_greeting = ast_calloc(decoded_length, sizeof(char));
+	if (!decoded_greeting) {
+		ast_ari_response_error(response, 500, "Internal Server Error", "Unable to allocate memory");
+		return;
+	}
+
+	num_bytes_decoded = ast_base64decode(decoded_greeting, encoded_greeting, decoded_length - 1);
+	if (num_bytes_decoded <= 0) {
+		ast_ari_response_error(response, 400, "Decoding Error", "Unable to decode base64");
+		goto data_cleanup;
+	}
+
+	if (!context) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid context");
+		goto data_cleanup;
+	}
+
+	if (!voicemail) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid voicemail");
+		goto data_cleanup;
+	}
+
+	if (!arg_format) {
+		strcpy(format, "wav");
+	} else if (ast_strlen_zero(arg_format) || strlen(arg_format) >= MAX_FORMAT_LEN - 1) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid format");
+		goto data_cleanup;
+	} else {
+		strcpy(format, arg_format);
+	}
+
+	result = ast_vm_change_greeting((const char*) decoded_greeting, num_bytes_decoded, context, voicemail, greeting, format);
+	if (result == -1) {
+		ast_ari_response_error(response, 400, "Unable to change greeting", "Unable to write file, invalid path");
+		goto data_cleanup;
+	} else if (result == -2) {
+		ast_ari_response_error(response, 404, "Unable to change greeting", "Unable to write file");
+		goto data_cleanup;
+	} else if (result == -4) {
+		ast_ari_response_error(response, 404, "Unable to change greeting", "Does not exist");
+		goto data_cleanup;
+	}
+
+	ast_ari_response_no_content(response);
+
+data_cleanup:
+	ast_free(decoded_greeting);
+}
+
+void ast_ari_wazo_remove_voicemail_greeting(struct ast_variable *headers,
+	struct ast_ari_wazo_remove_voicemail_greeting_args *args, struct ast_ari_response *response)
+{
+	int result = 0;
+	const char *greeting = args->greeting;
+	const char *context = args->context;
+	const char *voicemail = args->voicemail;
+
+	if (!greeting) {
+		ast_ari_response_error(response, 400, "Invalid argument", "No greeting specified");
+		return;
+	}
+
+	if (!validate_greeting(greeting)) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid greeting");
+		return;
+	}
+
+	if (!context) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid context");
+		return;
+	}
+
+	if (!voicemail) {
+		ast_ari_response_error(response, 400, "Invalid argument", "Invalid voicemail");
+		return;
+	}
+
+	result = ast_vm_remove_greeting(context, voicemail, greeting);
+	if (result == -1) {
+		ast_ari_response_error(response, 400, "Invalid argument", "unable to remove greeting");
+	} else if (result == -2) {
+		ast_ari_response_error(response, 400, "Invalid argument", "unable to remove greeting, unable to generate file path");
+	} else {
+		ast_ari_response_no_content(response);
+	}
+}
+
+/*
+ * Check if given greeting string is valid
+ */
+static int validate_greeting(const char *greeting) {
+      return !strcmp(greeting, "unavailable") || !strcmp(greeting, "busy") || !strcmp(greeting, "name");
+}
Index: asterisk-22.8.2/res/ari/resource_wazo.h
===================================================================
--- /dev/null
+++ asterisk-22.8.2/res/ari/resource_wazo.h
@@ -0,0 +1,200 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2016-2019 The Wazo Authors  (see the AUTHORS file)
+ *
+ * Etienne Lessard
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Generated file - declares stubs to be implemented in
+ * res/ari/resource_wazo.c
+ *
+ * Wazo resources
+ *
+ * \author Etienne Lessard
+ */
+
+/*
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * !!!!!                               DO NOT EDIT                        !!!!!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * This file is generated by a mustache template. Please see the original
+ * template in rest-api-templates/ari_resource.h.mustache
+ */
+
+#ifndef _ASTERISK_RESOURCE_WAZO_H
+#define _ASTERISK_RESOURCE_WAZO_H
+
+#include "asterisk/ari.h"
+
+/*! Argument struct for ast_ari_wazo_delete_voicemail_message() */
+struct ast_ari_wazo_delete_voicemail_message_args {
+	struct ast_json *body;
+};
+/*!
+ * \brief Body parsing function for /wazo/internal/voicemails/delete_msg.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_wazo_delete_voicemail_message_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_delete_voicemail_message_args *args);
+
+/*!
+ * \brief Delete a voicemail's message.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_wazo_delete_voicemail_message(struct ast_variable *headers, struct ast_ari_wazo_delete_voicemail_message_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_wazo_move_voicemail_message() */
+struct ast_ari_wazo_move_voicemail_message_args {
+	struct ast_json *body;
+};
+/*!
+ * \brief Body parsing function for /wazo/internal/voicemails/move_msg.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_wazo_move_voicemail_message_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_move_voicemail_message_args *args);
+
+/*!
+ * \brief Move a voicemail's message.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_wazo_move_voicemail_message(struct ast_variable *headers, struct ast_ari_wazo_move_voicemail_message_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_wazo_change_voicemail_greeting() */
+struct ast_ari_wazo_change_voicemail_greeting_args {
+	/*! Voicemail context; ex.: 'default' */
+	const char *context;
+	/*! Name of the voicemail */
+	const char *voicemail;
+	/*! 'busy', 'unavailable' or 'name' */
+	const char *greeting;
+	/*! File extension, without the leading '.'; ex.: 'wav', 'gsm', etc. (79 characters max). 'wav' by default */
+	const char *format;
+	/*! base64 encoded file data with key "greeting_base64" */
+	struct ast_json *body;
+};
+/*!
+ * \brief Body parsing function for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_wazo_change_voicemail_greeting_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_change_voicemail_greeting_args *args);
+
+/*!
+ * \brief Change a voicemail's greet message.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_wazo_change_voicemail_greeting(struct ast_variable *headers, struct ast_ari_wazo_change_voicemail_greeting_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_wazo_create_voicemail_greeting() */
+struct ast_ari_wazo_create_voicemail_greeting_args {
+	/*! Voicemail context; ex.: 'default' */
+	const char *context;
+	/*! Name of the voicemail */
+	const char *voicemail;
+	/*! 'busy', 'unavailable' or 'name' */
+	const char *greeting;
+	/*! File extension, without the leading '.'; ex.: 'wav', 'gsm', etc. (79 characters max). 'wav' by default */
+	const char *format;
+	/*! base64 encoded file data with key "greeting_base64" */
+	struct ast_json *body;
+};
+/*!
+ * \brief Body parsing function for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_wazo_create_voicemail_greeting_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_create_voicemail_greeting_args *args);
+
+/*!
+ * \brief Create a voicemail's greet message.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_wazo_create_voicemail_greeting(struct ast_variable *headers, struct ast_ari_wazo_create_voicemail_greeting_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_wazo_get_voicemail_greeting() */
+struct ast_ari_wazo_get_voicemail_greeting_args {
+	/*! Voicemail context; ex.: 'default' */
+	const char *context;
+	/*! Name of the voicemail */
+	const char *voicemail;
+	/*! 'busy', 'unavailable' or 'name' */
+	const char *greeting;
+	/*! File extension, without the leading '.'; ex.: 'wav', 'gsm', etc. (79 characters max). 'wav' if unspecified */
+	const char *format;
+};
+/*!
+ * \brief Body parsing function for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_wazo_get_voicemail_greeting_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_get_voicemail_greeting_args *args);
+
+/*!
+ * \brief Receive a voicemail's greet message.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_wazo_get_voicemail_greeting(struct ast_variable *headers, struct ast_ari_wazo_get_voicemail_greeting_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_wazo_remove_voicemail_greeting() */
+struct ast_ari_wazo_remove_voicemail_greeting_args {
+	/*! Voicemail context; ex.: 'default' */
+	const char *context;
+	/*! Name of the voicemail */
+	const char *voicemail;
+	/*! 'busy', 'unavailable' or 'name' */
+	const char *greeting;
+};
+/*!
+ * \brief Delete a voicemail's custom greet message.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_wazo_remove_voicemail_greeting(struct ast_variable *headers, struct ast_ari_wazo_remove_voicemail_greeting_args *args, struct ast_ari_response *response);
+
+#endif /* _ASTERISK_RESOURCE_WAZO_H */
Index: asterisk-22.8.2/res/res_ari_wazo.c
===================================================================
--- /dev/null
+++ asterisk-22.8.2/res/res_ari_wazo.c
@@ -0,0 +1,608 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2016-2019 The Wazo Authors  (see the AUTHORS file)
+ *
+ * Etienne Lessard
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * !!!!!                               DO NOT EDIT                        !!!!!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * This file is generated by a mustache template. Please see the original
+ * template in rest-api-templates/res_ari_resource.c.mustache
+ */
+
+/*! \file
+ *
+ * \brief Wazo resources
+ *
+ * \author Etienne Lessard
+ */
+
+/*** MODULEINFO
+	<depend type="module">res_ari</depend>
+	<depend type="module">res_ari_model</depend>
+	<depend type="module">res_stasis</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "asterisk/stasis_app.h"
+#include "ari/resource_wazo.h"
+#if defined(AST_DEVMODE)
+#include "ari/ari_model_validators.h"
+#endif
+
+#define MAX_VALS 128
+
+int ast_ari_wazo_delete_voicemail_message_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_delete_voicemail_message_args *args)
+{
+	/* Parse query parameters out of it */
+	return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /wazo/internal/voicemails/delete_msg.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_wazo_delete_voicemail_message_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+	struct ast_ari_wazo_delete_voicemail_message_args args = {};
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	args.body = body;
+	ast_ari_wazo_delete_voicemail_message(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 400: /* Bad request body */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /wazo/internal/voicemails/delete_msg\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /wazo/internal/voicemails/delete_msg\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+int ast_ari_wazo_move_voicemail_message_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_move_voicemail_message_args *args)
+{
+	/* Parse query parameters out of it */
+	return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /wazo/internal/voicemails/move_msg.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_wazo_move_voicemail_message_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+	struct ast_ari_wazo_move_voicemail_message_args args = {};
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	args.body = body;
+	ast_ari_wazo_move_voicemail_message(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 400: /* Bad request body */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /wazo/internal/voicemails/move_msg\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /wazo/internal/voicemails/move_msg\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+int ast_ari_wazo_change_voicemail_greeting_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_change_voicemail_greeting_args *args)
+{
+	struct ast_json *field;
+	/* Parse query parameters out of it */
+	field = ast_json_object_get(body, "format");
+	if (field) {
+		args->format = ast_json_string_get(field);
+	}
+	return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_wazo_change_voicemail_greeting_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+	struct ast_ari_wazo_change_voicemail_greeting_args args = {};
+	struct ast_variable *i;
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = get_params; i; i = i->next) {
+		if (strcmp(i->name, "format") == 0) {
+			args.format = (i->value);
+		} else
+		{}
+	}
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "context") == 0) {
+			args.context = (i->value);
+		} else
+		if (strcmp(i->name, "voicemail") == 0) {
+			args.voicemail = (i->value);
+		} else
+		if (strcmp(i->name, "greeting") == 0) {
+			args.greeting = (i->value);
+		} else
+		{}
+	}
+	args.body = body;
+	ast_ari_wazo_change_voicemail_greeting(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 400: /* Invalid parameters were passed */
+	case 404: /* Unable to save the voicemail message */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+int ast_ari_wazo_create_voicemail_greeting_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_create_voicemail_greeting_args *args)
+{
+	struct ast_json *field;
+	/* Parse query parameters out of it */
+	field = ast_json_object_get(body, "format");
+	if (field) {
+		args->format = ast_json_string_get(field);
+	}
+	return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_wazo_create_voicemail_greeting_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+	struct ast_ari_wazo_create_voicemail_greeting_args args = {};
+	struct ast_variable *i;
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = get_params; i; i = i->next) {
+		if (strcmp(i->name, "format") == 0) {
+			args.format = (i->value);
+		} else
+		{}
+	}
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "context") == 0) {
+			args.context = (i->value);
+		} else
+		if (strcmp(i->name, "voicemail") == 0) {
+			args.voicemail = (i->value);
+		} else
+		if (strcmp(i->name, "greeting") == 0) {
+			args.greeting = (i->value);
+		} else
+		{}
+	}
+	args.body = body;
+	ast_ari_wazo_create_voicemail_greeting(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 400: /* Invalid parameters were passed */
+	case 404: /* Unable to create the voicemail message */
+	case 409: /* Greeting already exists */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+int ast_ari_wazo_get_voicemail_greeting_parse_body(
+	struct ast_json *body,
+	struct ast_ari_wazo_get_voicemail_greeting_args *args)
+{
+	struct ast_json *field;
+	/* Parse query parameters out of it */
+	field = ast_json_object_get(body, "format");
+	if (field) {
+		args->format = ast_json_string_get(field);
+	}
+	return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_wazo_get_voicemail_greeting_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+	struct ast_ari_wazo_get_voicemail_greeting_args args = {};
+	struct ast_variable *i;
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = get_params; i; i = i->next) {
+		if (strcmp(i->name, "format") == 0) {
+			args.format = (i->value);
+		} else
+		{}
+	}
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "context") == 0) {
+			args.context = (i->value);
+		} else
+		if (strcmp(i->name, "voicemail") == 0) {
+			args.voicemail = (i->value);
+		} else
+		if (strcmp(i->name, "greeting") == 0) {
+			args.greeting = (i->value);
+		} else
+		{}
+	}
+	if (ast_ari_wazo_get_voicemail_greeting_parse_body(body, &args)) {
+		ast_ari_response_alloc_failed(response);
+		goto fin;
+	}
+	ast_ari_wazo_get_voicemail_greeting(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 400: /* Invalid parameters were passed */
+	case 404: /* Requested file was not found */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_encoded_file(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_wazo_remove_voicemail_greeting_cb(
+	struct ast_tcptls_session_instance *ser,
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+	struct ast_ari_wazo_remove_voicemail_greeting_args args = {};
+	struct ast_variable *i;
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "context") == 0) {
+			args.context = (i->value);
+		} else
+		if (strcmp(i->name, "voicemail") == 0) {
+			args.voicemail = (i->value);
+		} else
+		if (strcmp(i->name, "greeting") == 0) {
+			args.greeting = (i->value);
+		} else
+		{}
+	}
+	ast_ari_wazo_remove_voicemail_greeting(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 400: /* Could not delete object */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /wazo/internal/voicemails/{context}/{voicemail}/{greeting}\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+
+/*! \brief REST handler for /api-docs/wazo.json */
+static struct stasis_rest_handlers wazo_internal_voicemails_delete_msg = {
+	.path_segment = "delete_msg",
+	.callbacks = {
+		[AST_HTTP_POST] = ast_ari_wazo_delete_voicemail_message_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/wazo.json */
+static struct stasis_rest_handlers wazo_internal_voicemails_move_msg = {
+	.path_segment = "move_msg",
+	.callbacks = {
+		[AST_HTTP_POST] = ast_ari_wazo_move_voicemail_message_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/wazo.json */
+static struct stasis_rest_handlers wazo_internal_voicemails_context_voicemail_greeting = {
+	.path_segment = "greeting",
+	.is_wildcard = 1,
+	.callbacks = {
+		[AST_HTTP_PUT] = ast_ari_wazo_change_voicemail_greeting_cb,
+		[AST_HTTP_POST] = ast_ari_wazo_create_voicemail_greeting_cb,
+		[AST_HTTP_GET] = ast_ari_wazo_get_voicemail_greeting_cb,
+		[AST_HTTP_DELETE] = ast_ari_wazo_remove_voicemail_greeting_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/wazo.json */
+static struct stasis_rest_handlers wazo_internal_voicemails_context_voicemail = {
+	.path_segment = "voicemail",
+	.is_wildcard = 1,
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &wazo_internal_voicemails_context_voicemail_greeting, }
+};
+/*! \brief REST handler for /api-docs/wazo.json */
+static struct stasis_rest_handlers wazo_internal_voicemails_context = {
+	.path_segment = "context",
+	.is_wildcard = 1,
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &wazo_internal_voicemails_context_voicemail, }
+};
+/*! \brief REST handler for /api-docs/wazo.json */
+static struct stasis_rest_handlers wazo_internal_voicemails = {
+	.path_segment = "voicemails",
+	.callbacks = {
+	},
+	.num_children = 3,
+	.children = { &wazo_internal_voicemails_delete_msg,&wazo_internal_voicemails_move_msg,&wazo_internal_voicemails_context, }
+};
+/*! \brief REST handler for /api-docs/wazo.json */
+static struct stasis_rest_handlers wazo_internal = {
+	.path_segment = "internal",
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &wazo_internal_voicemails, }
+};
+/*! \brief REST handler for /api-docs/wazo.json */
+static struct stasis_rest_handlers wazo = {
+	.path_segment = "wazo",
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &wazo_internal, }
+};
+
+static int unload_module(void)
+{
+	ast_ari_remove_handler(&wazo);
+	return 0;
+}
+
+static int load_module(void)
+{
+	int res = 0;
+
+
+	res |= ast_ari_add_handler(&wazo);
+	if (res) {
+		unload_module();
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Wazo resources",
+	.support_level = AST_MODULE_SUPPORT_CORE,
+	.load = load_module,
+	.unload = unload_module,
+	.requires = "res_ari,res_ari_model,res_stasis",
+);
Index: asterisk-22.8.2/rest-api/api-docs/wazo.json
===================================================================
--- /dev/null
+++ asterisk-22.8.2/rest-api/api-docs/wazo.json
@@ -0,0 +1,296 @@
+{
+	"_copyright": "Copyright 2016-2019 The Wazo Authors  (see the AUTHORS file)",
+	"_author": "Etienne Lessard",
+	"apiVersion": "1.7.0",
+	"swaggerVersion": "1.1",
+	"basePath": "http://localhost:8088/ari",
+	"resourcePath": "/api-docs/wazo.{format}",
+	"apis": [
+		{
+			"path": "/wazo/internal/voicemails/delete_msg",
+			"description": "Delete a voicemail's message",
+			"operations": [
+				{
+					"httpMethod": "POST",
+					"summary": "Delete a voicemail's message.",
+					"nickname": "deleteVoicemailMessage",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "body",
+							"description": "",
+							"paramType": "body",
+							"required": true,
+							"dataType": "containers",
+							"allowMultiple": false
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Bad request body"
+						}
+					]
+				}
+			]
+		},
+		{
+			"path": "/wazo/internal/voicemails/move_msg",
+			"description": "Move a voicemail's message",
+			"operations": [
+				{
+					"httpMethod": "POST",
+					"summary": "Move a voicemail's message.",
+					"nickname": "moveVoicemailMessage",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "body",
+							"description": "",
+							"paramType": "body",
+							"required": true,
+							"dataType": "containers",
+							"allowMultiple": false
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Bad request body"
+						}
+					]
+				}
+			]
+		},
+		{
+			"path": "/wazo/internal/voicemails/{context}/{voicemail}/{greeting}",
+			"description": "Save a voicemail's greet message",
+			"operations": [
+				{
+					"httpMethod": "PUT",
+					"summary": "Change a voicemail's greet message.",
+					"nickname": "changeVoicemailGreeting",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "context",
+							"description": "Voicemail context; ex.: 'default'",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "voicemail",
+							"description": "Name of the voicemail",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "greeting",
+							"description": "'busy', 'unavailable' or 'name'",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "format",
+							"description": "File extension, without the leading '.'; ex.: 'wav', 'gsm', etc. (79 characters max). 'wav' by default",
+							"paramType": "query",
+							"required": false,
+							"default": "wav",
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "body",
+							"description": "base64 encoded file data with key \"greeting_base64\"",
+							"paramType": "body",
+							"required": true,
+							"dataType": "containers",
+							"allowMultiple": false
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Invalid parameters were passed"
+						},
+						{
+							"code": 404,
+							"reason": "Unable to save the voicemail message"
+						}
+					]
+				},
+				{
+					"httpMethod": "POST",
+					"summary": "Create a voicemail's greet message.",
+					"nickname": "createVoicemailGreeting",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "context",
+							"description": "Voicemail context; ex.: 'default'",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "voicemail",
+							"description": "Name of the voicemail",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "greeting",
+							"description": "'busy', 'unavailable' or 'name'",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "format",
+							"description": "File extension, without the leading '.'; ex.: 'wav', 'gsm', etc. (79 characters max). 'wav' by default",
+							"paramType": "query",
+							"required": false,
+							"default": "wav",
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "body",
+							"description": "base64 encoded file data with key \"greeting_base64\"",
+							"paramType": "body",
+							"required": true,
+							"dataType": "containers",
+							"allowMultiple": false
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Invalid parameters were passed"
+						},
+						{
+							"code": 404,
+							"reason": "Unable to create the voicemail message"
+						},
+						{
+							"code": 409,
+							"reason": "Greeting already exists"
+						}
+					]
+				},
+				{
+					"httpMethod": "GET",
+					"summary": "Receive a voicemail's greet message.",
+					"nickname": "getVoicemailGreeting",
+					"responseClass": "EncodedFile",
+					"parameters": [
+						{
+							"name": "context",
+							"description": "Voicemail context; ex.: 'default'",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "voicemail",
+							"description": "Name of the voicemail",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "greeting",
+							"description": "'busy', 'unavailable' or 'name'",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "format",
+							"description": "File extension, without the leading '.'; ex.: 'wav', 'gsm', etc. (79 characters max). 'wav' if unspecified",
+							"paramType": "query",
+							"required": false,
+							"default": "wav",
+							"dataType": "string",
+							"allowMultiple": false
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Invalid parameters were passed"
+						},
+						{
+							"code": 404,
+							"reason": "Requested file was not found"
+						}
+					]
+				},
+				{
+					"httpMethod": "DELETE",
+					"summary": "Delete a voicemail's custom greet message.",
+					"nickname": "removeVoicemailGreeting",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "context",
+							"description": "Voicemail context; ex.: 'default'",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "voicemail",
+							"description": "Name of the voicemail",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						},
+						{
+							"name": "greeting",
+							"description": "'busy', 'unavailable' or 'name'",
+							"paramType": "path",
+							"required": true,
+							"dataType": "string",
+							"allowMultiple": false
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Could not delete object"
+						}
+					]
+				}
+			]
+		}
+	],
+	"models": {
+		"EncodedFile": {
+			"id": "EncodedFile",
+			"description": "Contains the encoded file",
+			"properties": {
+				"greeting_base64": {
+					"type": "string",
+					"description": "The file data encoded as base64",
+					"required": true
+				}
+			}
+		}
+	}
+}
Index: asterisk-22.8.2/rest-api/resources.json
===================================================================
--- asterisk-22.8.2.orig/rest-api/resources.json
+++ asterisk-22.8.2/rest-api/resources.json
@@ -49,6 +49,10 @@
 		{
 			"path": "/api-docs/applications.{format}",
 			"description": "Stasis application resources"
+		},
+		{
+			"path": "/api-docs/wazo.{format}",
+			"description": "Wazo resources"
 		}
 	]
 }
Index: asterisk-22.8.2/include/asterisk/app.h
===================================================================
--- asterisk-22.8.2.orig/include/asterisk/app.h
+++ asterisk-22.8.2/include/asterisk/app.h
@@ -518,6 +518,19 @@ typedef int (ast_vm_msg_forward_fn)(cons
 typedef int (ast_vm_msg_play_fn)(struct ast_channel *chan, const char *mailbox,
 	const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb *cb);
 
+
+typedef int (ast_vm_change_greeting_fn)(const char *data, unsigned int length, const char* absence, const char* context, const char* voicemail,
+		const char* format);
+
+typedef int (ast_vm_create_greeting_fn)(const char *data, unsigned int length, const char* absence, const char* context, const char* voicemail,
+		const char* format);
+
+typedef int (ast_vm_remove_greeting_fn)(const char* absence, const char* context, const char* voicemail);
+
+typedef int (ast_vm_get_greeting_fn)(char** data,  const char* absence, const char* context, const char* voicemail,
+		const char* format);
+
+
 #define VM_MODULE_VERSION 2
 
 /*! \brief Voicemail function table definition. */
@@ -546,8 +559,13 @@ struct ast_vm_functions {
 	ast_vm_msg_remove_fn *msg_remove;
 	ast_vm_msg_forward_fn *msg_forward;
 	ast_vm_msg_play_fn *msg_play;
+	ast_vm_change_greeting_fn *change_greeting;
+	ast_vm_create_greeting_fn *create_greeting;
+	ast_vm_get_greeting_fn *get_greeting;
+	ast_vm_remove_greeting_fn *remove_greeting;
 };
 
+
 /*!
  * \brief Determine if a voicemail provider is registered.
  * \since 12.0.0
@@ -569,6 +587,46 @@ int ast_vm_is_registered(void);
  */
 int __ast_vm_register(const struct ast_vm_functions *vm_table, struct ast_module *module);
 
+/*!
+ * \brief Change a greeting to a file
+ *
+ * \param file the path to the file
+ * \param data the data to be written
+ * @return 0 on success, negative otherwise
+ */
+int ast_vm_change_greeting(const char* data, unsigned int length,  const char* absence, const char* context,
+		const char* voicemail, const char* extension);
+
+/*!
+ * \brief Create a greeting to a file
+ *
+ * \param file the path to the file
+ * \param data the data to be written
+ * @return 0 on success, negative otherwise
+ */
+int ast_vm_create_greeting(const char* data, unsigned int length,  const char* absence, const char* context,
+		const char* voicemail, const char* extension);
+
+/*!
+ * \brief Retrieve a greeting file
+ *
+ * \param file the path to the file
+ * \param data A buffer where the file's data will be held
+ * @return The length of the data buffer, negative number on error
+ */
+int ast_vm_get_greeting(char** data, const char* absence, const char* context, const char* voicemail,
+		const char* extension);
+
+/*!
+ * \brief Delete a greeting file
+ *
+ * \param file the path to the file
+ * \param data A buffer where the file's data will be held
+ * @return The length of the data buffer, negative number on error
+ */
+int ast_vm_remove_greeting(const char* absence, const char* context, const char* voicemail);
+
+
 /*! \brief See \ref __ast_vm_register() */
 #define ast_vm_register(vm_table) __ast_vm_register(vm_table, AST_MODULE_SELF)
 
@@ -1527,6 +1585,7 @@ struct stasis_topic *ast_queue_topic_all
  */
 struct stasis_topic *ast_queue_topic(const char *queuename);
 
+
 /*!
  * \brief Initialize the application core
  * \retval 0 Success
Index: asterisk-22.8.2/main/app.c
===================================================================
--- asterisk-22.8.2.orig/main/app.c
+++ asterisk-22.8.2/main/app.c
@@ -736,6 +736,58 @@ int ast_vm_msg_play(struct ast_channel *
 	return res;
 }
 
+int ast_vm_change_greeting(const char *data,
+		unsigned int length,
+		const char *absence,
+		const char *context,
+		const char *voicemail,
+		const char *extension)
+{
+	int res = 0;
+
+	VM_API_CALL(res, change_greeting, (data, length, absence, context, voicemail, extension));
+
+	return res;
+}
+
+int ast_vm_create_greeting(const char* data,
+		unsigned int length,
+		const char* absence,
+		const char* context,
+		const char* voicemail,
+		const char* extension)
+{
+	int res = 0;
+
+	VM_API_CALL(res, create_greeting, (data, length, absence, context, voicemail, extension));
+
+	return res;
+}
+
+int ast_vm_remove_greeting(const char *absence,
+		const char *context, const char *voicemail)
+{
+	int res = 0;
+
+	VM_API_CALL(res, remove_greeting, (absence, context, voicemail));
+
+	return res;
+
+}
+
+int ast_vm_get_greeting(char **data,
+		const char *absence,
+		const char *context,
+		const char *voicemail,
+		const char *extension)
+{
+	int res = 0;
+
+	VM_API_CALL(res, get_greeting, (data, absence, context, voicemail, extension));
+
+	return res;
+}
+
 #ifdef TEST_FRAMEWORK
 int ast_vm_test_create_user(const char *context, const char *mailbox)
 {
Index: asterisk-22.8.2/apps/app_voicemail.c
===================================================================
--- asterisk-22.8.2.orig/apps/app_voicemail.c
+++ asterisk-22.8.2/apps/app_voicemail.c
@@ -713,6 +713,10 @@ static AST_LIST_HEAD_STATIC(vmstates, vm
 #define ENDL "\n"
 #endif
 
+#define VM_TYPE_UNAVAILABLE "unavailable"
+#define VM_TYPE_BUSY "busy"
+#define VM_TYPE_GREET "name"
+
 #define MAX_DATETIME_FORMAT	512
 #define MAX_NUM_CID_CONTEXTS 10
 
@@ -1264,6 +1268,13 @@ static int vm_msg_forward(const char *fr
 static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder);
 static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
 static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb);
+static int vm_change_greeting(const char* data, unsigned int length, const char* context, const char* voicemail, const char* greeting, const char* format);
+static int vm_create_greeting(const char* data, unsigned int length, const char* context, const char* voicemail, const char* greeting, const char* format);
+static int vm_get_greeting(char** data, const char* context, const char* voicemail, const char* greeting, const char* format);
+static int get_vm_greeting_path(char* result_path, const char* context, const char* voicemail, const char* greeting, const char* format);
+static int get_vm_greeting_file_name_path(char* result_path, const char* context, const char* voicemail, const char* greeting);
+static int vm_remove_greeting(const char* context, const char* voicemail, const char* greeting);
+
 
 #ifdef TEST_FRAMEWORK
 static int vm_test_destroy_user(const char *context, const char *mailbox);
@@ -16258,6 +16269,10 @@ static const struct ast_vm_functions vm_
 	.msg_remove = vm_msg_remove,
 	.msg_forward = vm_msg_forward,
 	.msg_play = vm_msg_play,
+	.change_greeting = vm_change_greeting,
+	.create_greeting = vm_create_greeting,
+	.get_greeting = vm_get_greeting,
+	.remove_greeting = vm_remove_greeting
 };
 
 static const struct ast_vm_greeter_functions vm_greeter_table = {
@@ -17846,6 +17861,197 @@ play2_msg_cleanup:
 	return res;
 }
 
+static int vm_create_greeting(const char* raw_greeting_audio,
+	unsigned int length,
+	const char* context,
+	const char* voicemail,
+	const char* greeting,
+	const char* format)
+{
+	char full_path[PATH_MAX];
+	char dir_path[PATH_MAX];
+	char file_path[PATH_MAX];
+	FILE *f = NULL;
+	size_t num_bytes_written = 0;
+
+	int make_path_success = get_vm_greeting_path(full_path, context, voicemail, greeting, format);
+	if (make_path_success != 0) {
+		return -1;
+	}
+
+	if (create_dirpath(dir_path, PATH_MAX, context, voicemail, "") == -1) {
+			ast_log(LOG_ERROR, "Unable to create directory\n");
+			return -1;
+	}
+
+	get_vm_greeting_file_name_path(file_path, context, voicemail, greeting);
+
+	if (ast_fileexists(file_path, NULL, NULL)) {
+		ast_log(LOG_WARNING, "attempted to create existing file: %s\n", full_path);
+		return -4;
+	}
+
+	f = fopen(full_path, "wb");
+	if (!f) {
+		ast_log(LOG_ERROR, "unable to open file at path %s\n", full_path);
+		return -2;
+	}
+
+	num_bytes_written = fwrite(raw_greeting_audio, length, sizeof(char), f);
+	if (num_bytes_written < 0) {
+		ast_log(LOG_ERROR, "unable to write file at path %s\n", full_path);
+		fclose(f);
+		return -3;
+	}
+
+	fclose(f);
+	return 0;
+}
+
+static int vm_change_greeting(const char* raw_greeting_audio,
+	unsigned int length,
+	const char* context,
+	const char* voicemail,
+	const char* greeting,
+	const char* format)
+{
+	char full_path[PATH_MAX];
+	char file_path[PATH_MAX];
+	FILE *f = NULL;
+	size_t num_bytes_written = 0;
+
+	int make_path_success = get_vm_greeting_path(full_path, context, voicemail, greeting, format);
+	if (make_path_success != 0) {
+		return -1;
+	}
+
+	get_vm_greeting_file_name_path(file_path, context, voicemail, greeting);
+
+	if (!ast_fileexists(file_path, NULL, NULL)) {
+		ast_log(LOG_WARNING, "attempted to change non-existing file: %s\n", full_path);
+		return -4;
+	}
+
+	f = fopen(full_path, "wb");
+	if (!f) {
+		ast_log(LOG_ERROR, "unable to open file at path %s\n", full_path);
+		return -2;
+	}
+
+	num_bytes_written = fwrite(raw_greeting_audio, length, sizeof(char), f);
+	if (num_bytes_written < 0) {
+		ast_log(LOG_ERROR, "unable to write file at path %s\n", full_path);
+		fclose(f);
+		return -3;
+	}
+
+	fclose(f);
+	return 0;
+}
+
+static int vm_get_greeting(char** raw_greeting_audio,
+	const char *context,
+	const char *voicemail,
+	const char *greeting,
+	const char *format)
+ {
+	char path[PATH_MAX];
+	FILE *f = NULL;
+	long f_size = 0;
+	size_t n = 0;
+
+	int make_path_success = get_vm_greeting_path(path, context, voicemail, greeting, format);
+	if (make_path_success != 0) {
+		return -1;
+	}
+
+	f = fopen(path, "r");
+	if (!f) {
+		ast_log(LOG_ERROR, "unable to read file at path %s\n", path);
+		return -2;
+	}
+
+	fseek(f, 0, SEEK_END);
+	f_size = ftell(f);
+	rewind(f);
+	*raw_greeting_audio = ast_calloc(f_size + 1, sizeof(char));
+	if (!*raw_greeting_audio) {
+		ast_log(LOG_ERROR, "unable to allocate memory for data\n");
+		fclose(f);
+		return -3;
+	}
+	n = fread(*raw_greeting_audio, sizeof(char), f_size, f);
+	fclose(f);
+	return n;
+}
+
+static int vm_remove_greeting(const char *context,
+	const char *voicemail,
+	const char *greeting)
+{
+	char path[PATH_MAX];
+
+	/* special case: get_vm_greeting_path() is cannot be used here */
+	if (!strcmp(greeting, VM_TYPE_BUSY)) {
+		snprintf(path, PATH_MAX, "%s%s/%s/busy", VM_SPOOL_DIR, context, voicemail);
+	} else if (!strcmp(greeting, VM_TYPE_UNAVAILABLE)) {
+		snprintf(path, PATH_MAX, "%s%s/%s/unavail", VM_SPOOL_DIR, context, voicemail);
+	} else if (!strcmp(greeting, VM_TYPE_GREET)) {
+		snprintf(path, PATH_MAX, "%s%s/%s/greet", VM_SPOOL_DIR, context, voicemail);
+	} else {
+		return -2;
+	}
+
+	if (!ast_fileexists(path, NULL, NULL)) {
+		return 0; /* if file does not exist, delete operation is considered a success */
+	}
+
+	return ast_filedelete(path, NULL); /* returns -1 on error */
+}
+
+static int get_vm_greeting_path(char *result_path,
+	const char *context,
+	const char *voicemail,
+	const char *greeting,
+	const char *format)
+{
+	if (!strcmp(greeting, VM_TYPE_BUSY)) {
+		snprintf(result_path, PATH_MAX, "%s%s/%s/busy.%s", VM_SPOOL_DIR, context, voicemail, format);
+	} else if (!strcmp(greeting, VM_TYPE_UNAVAILABLE)) {
+		snprintf(result_path, PATH_MAX, "%s%s/%s/unavail.%s", VM_SPOOL_DIR, context, voicemail, format);
+	} else if (!strcmp(greeting, VM_TYPE_GREET)) {
+		snprintf(result_path, PATH_MAX, "%s%s/%s/greet.%s", VM_SPOOL_DIR, context, voicemail, format);
+	} else {
+		return -1;
+	}
+
+	if (get_vm_greeting_file_name_path(result_path, context, voicemail, greeting) == -1) {
+		return -1;
+	}
+	strcat(result_path, ".");
+	strcat(result_path, format);
+
+	return 0;
+}
+
+static int get_vm_greeting_file_name_path(char* result_path,
+	const char* context,
+	const char* voicemail,
+	const char* greeting)
+{
+	if (!strcmp(greeting, VM_TYPE_BUSY)) {
+		snprintf(result_path, PATH_MAX, "%s%s/%s/busy", VM_SPOOL_DIR, context, voicemail);
+	} else if (!strcmp(greeting, VM_TYPE_UNAVAILABLE)) {
+		snprintf(result_path, PATH_MAX, "%s%s/%s/unavail", VM_SPOOL_DIR, context, voicemail);
+	} else if (!strcmp(greeting, VM_TYPE_GREET)) {
+		snprintf(result_path, PATH_MAX, "%s%s/%s/greet", VM_SPOOL_DIR, context, voicemail);
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+
 /* This is a workaround so that menuselect displays a proper description
  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
  */
Index: asterisk-22.8.2/main/http.c
===================================================================
--- asterisk-22.8.2.orig/main/http.c
+++ asterisk-22.8.2/main/http.c
@@ -82,7 +82,7 @@
 
 /*! Maximum application/json or application/x-www-form-urlencoded body content length. */
 #if !defined(LOW_MEMORY)
-#define MAX_CONTENT_LENGTH 40960
+#define MAX_CONTENT_LENGTH 2097152 /* allows for over one minute of audio @ 1000Hz */
 #else
 #define MAX_CONTENT_LENGTH 1024
 #endif	/* !defined(LOW_MEMORY) */
Index: asterisk-22.8.2/res/ari/ari_model_validators.c
===================================================================
--- asterisk-22.8.2.orig/res/ari/ari_model_validators.c
+++ asterisk-22.8.2/res/ari/ari_model_validators.c
@@ -8744,3 +8744,41 @@ ari_validator ast_ari_validate_applicati
 {
 	return ast_ari_validate_application;
 }
+
+int ast_ari_validate_encoded_file(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_greeting_base64 = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("greeting_base64", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_greeting_base64 = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI EncodedFile field greeting_base64 failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI EncodedFile has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_greeting_base64) {
+		ast_log(LOG_ERROR, "ARI EncodedFile missing required field greeting_base64\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_encoded_file_fn(void)
+{
+	return ast_ari_validate_encoded_file;
+}
Index: asterisk-22.8.2/res/ari/ari_model_validators.h
===================================================================
--- asterisk-22.8.2.orig/res/ari/ari_model_validators.h
+++ asterisk-22.8.2/res/ari/ari_model_validators.h
@@ -1503,6 +1503,22 @@ int ast_ari_validate_application(struct
  */
 ari_validator ast_ari_validate_application_fn(void);
 
+/*!
+ * \brief Validator for EncodedFile.
+ *
+ * Contains the encoded file
+ *
+ * \param json JSON object to validate.
+ * \retval True (non-zero) if valid.
+ * \retval False (zero) if invalid.
+ */
+int ast_ari_validate_encoded_file(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_encoded_file().
+ */
+ari_validator ast_ari_validate_encoded_file_fn(void);
+
 /*
  * JSON models
  *
@@ -2061,6 +2077,8 @@ ari_validator ast_ari_validate_applicati
  * - events_allowed: List[object] (required)
  * - events_disallowed: List[object] (required)
  * - name: string (required)
+ * EncodedFile
+ * - greeting_base64: string (required)
  */
 
 #endif /* _ASTERISK_ARI_MODEL_H */
