diff --git a/README.md b/README.md
index 40636a4cf..a85785026 100644
--- a/README.md
+++ b/README.md
@@ -16,14 +16,15 @@
 * [Pricing](#pricing)
 * [Installation](#installation)
 * [Register ClassifAI account](#register-classifai-account)
-* [Set Up IBM Watson NLU Language Processing](#set-up-language-processing-via-ibm-watson)
-* [Set Up OpenAI ChatGPT Language Processing](#set-up-language-processing-via-openai-chatgpt)
-* [Set Up Google AI (Gemini API) Language Processing](#set-up-language-processing-via-google-ai-gemini-api)
-* [Set Up OpenAI Embeddings Language Processing](#set-up-language-processing-via-openai-embeddings)
-* [Set Up OpenAI Whisper Language Processing](#set-up-language-processing-via-openai-whisper)
-* [Set Up Azure AI Language Processing](#set-up-language-processing-via-microsoft-azure)
-* [Set Up Azure AI Vision Image Processing](#set-up-image-processing-via-microsoft-azure)
-* [Set Up OpenAI DALL·E Image Processing](#set-up-image-processing-via-openai)
+* [Set Up IBM Watson NLU Language Processing](#set-up-classification-via-ibm-watson)
+* [Set Up OpenAI ChatGPT Language Processing](#set-up-language-processing-features-via-openai-chatgpt)
+* [Set Up Azure OpenAI Language Processing](#set-up-language-processing-features-via-azure-openai)
+* [Set Up Google AI (Gemini API) Language Processing](#set-up-language-processing-features-via-google-ai-gemini-api)
+* [Set Up OpenAI Embeddings Language Processing](#set-up-classification-via-openai-embeddings)
+* [Set Up OpenAI Whisper Language Processing](#set-up-audio-transcripts-generation-via-openai-whisper)
+* [Set Up Azure AI Language Processing](#set-up-text-to-speech-via-microsoft-azure)
+* [Set Up Azure AI Vision Image Processing](#set-up-image-processing-features-via-microsoft-azure)
+* [Set Up OpenAI DALL·E Image Processing](#set-up-image-generation-via-openai)
 * [Set Up OpenAI Moderation Language Processing](#set-up-comment-moderation-via-openai-moderation)
 * [Set Up Azure AI Personalizer Recommended Content](#set-up-recommended-content-via-microsoft-azure-ai-personalizer)
 * [WP CLI Commands](#wp-cli-commands)
@@ -34,13 +35,13 @@
 
 ## Overview
 
-Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Microsoft Azure AI](https://azure.microsoft.com/en-us/overview/ai-platform/), and [IBM Watson](https://www.ibm.com/watson) to augment your WordPress-powered websites.  Publish content faster while improving SEO performance and increasing audience engagement.  ClassifAI integrates Artificial Intelligence and Machine Learning technologies to lighten your workload and eliminate tedious tasks, giving you more time to create original content that matters.
+Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Microsoft Azure AI](https://azure.microsoft.com/en-us/overview/ai-platform/), [Google Gemini](https://ai.google.dev/) and [IBM Watson](https://www.ibm.com/watson) to augment your WordPress-powered websites.  Publish content faster while improving SEO performance and increasing audience engagement.  ClassifAI integrates Artificial Intelligence and Machine Learning technologies to lighten your workload and eliminate tedious tasks, giving you more time to create original content that matters.
 
 ## Features
 
-* Generate a summary of post content and store it as an excerpt using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat) and [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
-* Generate titles from post content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat) and [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
-* Expand or condense text content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat) and [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
+* Generate a summary of post content and store it as an excerpt using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
+* Generate titles from post content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
+* Expand or condense text content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
 * Generate new images on demand to use in-content or as a featured image using [OpenAI's DALL·E API](https://platform.openai.com/docs/guides/images)
 * Generate transcripts of audio files using [OpenAI's Whisper API](https://platform.openai.com/docs/guides/speech-to-text)
 * Moderate incoming comments for sensitive content using [OpenAI's Moderation API](https://platform.openai.com/docs/guides/moderation)
@@ -74,11 +75,12 @@ Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Micro
 * To utilize the NLU Language Processing functionality, you will need an active [IBM Watson](https://cloud.ibm.com/registration) account.
 * To utilize the ChatGPT, Embeddings, or Whisper Language Processing functionality or DALL·E Image Processing functionality, you will need an active [OpenAI](https://platform.openai.com/signup) account.
 * To utilize the Azure AI Vision Image Processing functionality or Text to Speech Language Processing functionality, you will need an active [Microsoft Azure](https://signup.azure.com/signup) account.
+* To utilize the Azure OpenAI Language Processing functionality, you will need an active [Microsoft Azure](https://signup.azure.com/signup) account and you will need to [apply](https://aka.ms/oai/access) for OpenAI access.
 * To utilize the Google Gemini Language Processing functionality, you will need an active [Google Gemini](https://ai.google.dev/tutorials/setup) account.
 
 ## Pricing
 
-Note that there is no cost to using ClassifAI itself. Both IBM Watson and Microsoft Azure have free plans for their AI services, but above those free plans there are paid levels as well.  So if you expect to process a high volume of content, then you'll want to review the pricing plans for these services to understand if you'll incur any costs.  For the most part, both services' free plans are quite generous and should at least allow for testing ClassifAI to better understand its featureset and could at best allow for totally free usage. OpenAI has a limited trial option that can be used for testing but will require a valid paid plan after that.
+Note that there is no cost to using ClassifAI itself. Both IBM Watson and Microsoft Azure have free plans for some of their AI services, but above those free plans there are paid levels as well.  So if you expect to process a high volume of content, then you'll want to review the pricing plans for these services to understand if you'll incur any costs.  For the most part, both services' free plans are quite generous and should at least allow for testing ClassifAI to better understand its featureset and could at best allow for totally free usage. OpenAI has a limited trial option that can be used for testing but will require a valid paid plan after that.
 
 IBM Watson's Natural Language Understanding ("NLU"), which is one of the providers that powers the classification feature, has a ["lite" pricing tier](https://www.ibm.com/cloud/watson-natural-language-understanding/pricing) that offers 30,000 free NLU items per month.
 
@@ -90,6 +92,8 @@ Microsoft Azure AI Speech, which is one of the providers that powers the text to
 
 Microsoft Azure AI Personalizer, which is one of the providers that powers the recommended content feature, has a ["free" pricing tier](https://azure.microsoft.com/en-us/pricing/details/cognitive-services/personalizer/) that offers 50,000 transactions per month.
 
+Microsoft Azure OpenAI, which is one of the providers that powers the title generation, excerpt generation and content resizing features, has a [pay per usage](https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/) plan.
+
 Google Gemini, which is one of the providers that powers the title generation, excerpt generation and content resizing features, has a ["free" pricing tier](https://ai.google.dev/pricing) that offers 60 queries per minute.
 
 ## Installation
@@ -244,6 +248,43 @@ IBM Watson's [Categories](https://cloud.ibm.com/docs/natural-language-understand
 * With this block selected, select the AI icon in the toolbar and choose to either expand or condense the text.
 * In the modal that pops up, select one of the options.
 
+## Set Up Language Processing Features (via Azure OpenAI)
+
+### 1. Sign up for Azure services
+
+* [Register for a Microsoft Azure account](https://azure.microsoft.com/en-us/free/) or sign into your existing one.
+* [Request access](https://aka.ms/oai/access) to Azure OpenAI, if not already granted.
+* Log into your account and create a new [*Azure OpenAI resource*](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource) if you do not already have one.
+* Copy the name you chose for the deployment when deploying the resource in the previous step.
+* Click `Keys and Endpoint` in the left hand Resource Management menu to get the endpoint for this resource.
+* Click the copy icon next to `KEY 1` to copy the API Key credential for this resource.
+
+### 2. Configure API Keys under Tools > ClassifAI > Language Processing > Title Generation, Excerpt Generation or Content Resizing
+
+* Select **Azure OpenAI** in the provider dropdown.
+* Enter your endpoint you copied from the above step into the `Endpoint URL` field.
+* Enter your API Key copied from the above step into the `API key` field.
+* Enter your deployment name copied from the above step into the `Deployment name` field.
+
+### 3. Enable specific Language Processing features
+
+* Check the "Enable" checkbox in above screen.
+* Set the other options as needed.
+* Save changes and ensure a success message is shown. An error will show if API authentication fails.
+
+### 4. Edit a content type to test enabled features
+
+* To test excerpt generation, edit (or create) an item that supports excerpts.
+* Ensure this item has content saved.
+* Open the Excerpt panel in the sidebar and click on `Generate Excerpt`.
+* To test title generation, edit (or create) an item that supports titles.
+* Ensure this item has content saved.
+* Open the Summary panel in the sidebar and click on `Generate titles`.
+* To test content resizing, edit (or create) an item. Note: only the block editor is supported.
+* Add a paragraph block with some content.
+* With this block selected, select the AI icon in the toolbar and choose to either expand or condense the text.
+* In the modal that pops up, select one of the options.
+
 ## Set Up Language Processing Features (via Google AI (Gemini API))
 
 ### 1. Sign up for Google AI
diff --git a/includes/Classifai/Features/ContentResizing.php b/includes/Classifai/Features/ContentResizing.php
index 7f186a9fa..6a6f9da73 100644
--- a/includes/Classifai/Features/ContentResizing.php
+++ b/includes/Classifai/Features/ContentResizing.php
@@ -2,6 +2,7 @@
 
 namespace Classifai\Features;
 
+use Classifai\Providers\Azure\OpenAI;
 use Classifai\Providers\GoogleAI\GeminiAPI;
 use Classifai\Providers\OpenAI\ChatGPT;
 use Classifai\Services\LanguageProcessing;
@@ -10,7 +11,6 @@
 use WP_Error;
 
 use function Classifai\sanitize_prompts;
-use function Classifai\sanitize_number_of_responses_field;
 use function Classifai\get_asset_info;
 
 /**
@@ -51,6 +51,7 @@ public function __construct() {
 		$this->supported_providers = [
 			ChatGPT::ID   => __( 'OpenAI ChatGPT', 'classifai' ),
 			GeminiAPI::ID => __( 'Google AI (Gemini API)', 'classifai' ),
+			OpenAI::ID    => __( 'Azure OpenAI', 'classifai' ),
 		];
 	}
 
diff --git a/includes/Classifai/Features/ExcerptGeneration.php b/includes/Classifai/Features/ExcerptGeneration.php
index c08d3278e..2a33db41b 100644
--- a/includes/Classifai/Features/ExcerptGeneration.php
+++ b/includes/Classifai/Features/ExcerptGeneration.php
@@ -2,9 +2,10 @@
 
 namespace Classifai\Features;
 
-use Classifai\Providers\GoogleAI\GeminiAPI;
 use Classifai\Services\LanguageProcessing;
+use Classifai\Providers\GoogleAI\GeminiAPI;
 use Classifai\Providers\OpenAI\ChatGPT;
+use Classifai\Providers\Azure\OpenAI;
 use WP_REST_Server;
 use WP_REST_Request;
 use WP_Error;
@@ -43,6 +44,7 @@ public function __construct() {
 		$this->supported_providers = [
 			ChatGPT::ID   => __( 'OpenAI ChatGPT', 'classifai' ),
 			GeminiAPI::ID => __( 'Google AI (Gemini API)', 'classifai' ),
+			OpenAI::ID    => __( 'Azure OpenAI', 'classifai' ),
 		];
 	}
 
diff --git a/includes/Classifai/Features/TitleGeneration.php b/includes/Classifai/Features/TitleGeneration.php
index 6c88394c3..3ceb9a1a7 100644
--- a/includes/Classifai/Features/TitleGeneration.php
+++ b/includes/Classifai/Features/TitleGeneration.php
@@ -2,9 +2,10 @@
 
 namespace Classifai\Features;
 
+use Classifai\Providers\Azure\OpenAI;
 use Classifai\Providers\GoogleAI\GeminiAPI;
-use Classifai\Services\LanguageProcessing;
 use Classifai\Providers\OpenAI\ChatGPT;
+use Classifai\Services\LanguageProcessing;
 use WP_REST_Server;
 use WP_REST_Request;
 use WP_Error;
@@ -43,6 +44,7 @@ public function __construct() {
 		$this->supported_providers = [
 			ChatGPT::ID   => __( 'OpenAI ChatGPT', 'classifai' ),
 			GeminiAPI::ID => __( 'Google AI (Gemini API)', 'classifai' ),
+			OpenAI::ID    => __( 'Azure OpenAI', 'classifai' ),
 		];
 	}
 
diff --git a/includes/Classifai/Providers/Azure/OpenAI.php b/includes/Classifai/Providers/Azure/OpenAI.php
new file mode 100644
index 000000000..7a542a2b7
--- /dev/null
+++ b/includes/Classifai/Providers/Azure/OpenAI.php
@@ -0,0 +1,762 @@
+<?php
+/**
+ * Azure OpenAI integration
+ */
+
+namespace Classifai\Providers\Azure;
+
+use Classifai\Features\ContentResizing;
+use Classifai\Features\ExcerptGeneration;
+use Classifai\Features\TitleGeneration;
+use Classifai\Providers\Provider;
+use Classifai\Normalizer;
+use WP_Error;
+
+use function Classifai\get_default_prompt;
+use function Classifai\sanitize_number_of_responses_field;
+
+class OpenAI extends Provider {
+
+	/**
+	 * Provider ID
+	 *
+	 * @var string
+	 */
+	const ID = 'azure_openai';
+
+	/**
+	 * Chat completion URL fragment.
+	 *
+	 * @var string
+	 */
+	protected $chat_completion_url = 'openai/deployments/{deployment-id}/chat/completions';
+
+	/**
+	 * Completion URL fragment.
+	 *
+	 * @var string
+	 */
+	protected $completion_url = 'openai/deployments/{deployment-id}/completions';
+
+	/**
+	 * Chat completion API version.
+	 *
+	 * @var string
+	 */
+	protected $chat_completion_api_version = '2023-05-15';
+
+	/**
+	 * Completion API version.
+	 *
+	 * @var string
+	 */
+	protected $completion_api_version = '2023-05-15';
+
+	/**
+	 * GeminiAPI constructor.
+	 *
+	 * @param \Classifai\Features\Feature $feature_instance The feature instance.
+	 */
+	public function __construct( $feature_instance = null ) {
+		$this->feature_instance = $feature_instance;
+	}
+
+	/**
+	 * Render the provider fields.
+	 */
+	public function render_provider_fields() {
+		$settings = $this->feature_instance->get_settings( static::ID );
+
+		add_settings_field(
+			static::ID . '_endpoint_url',
+			esc_html__( 'Endpoint URL', 'classifai' ),
+			[ $this->feature_instance, 'render_input' ],
+			$this->feature_instance->get_option_name(),
+			$this->feature_instance->get_option_name() . '_section',
+			[
+				'option_index'  => static::ID,
+				'label_for'     => 'endpoint_url',
+				'input_type'    => 'text',
+				'default_value' => $settings['endpoint_url'],
+				'description'   => __( 'Supported protocol and hostname endpoints, e.g., <code>https://EXAMPLE.openai.azure.com</code>.', 'classifai' ),
+				'class'         => 'large-text classifai-provider-field hidden provider-scope-' . static::ID,
+			]
+		);
+
+		add_settings_field(
+			static::ID . '_api_key',
+			esc_html__( 'API key', 'classifai' ),
+			[ $this->feature_instance, 'render_input' ],
+			$this->feature_instance->get_option_name(),
+			$this->feature_instance->get_option_name() . '_section',
+			[
+				'option_index'  => static::ID,
+				'label_for'     => 'api_key',
+				'input_type'    => 'password',
+				'default_value' => $settings['api_key'],
+				'class'         => 'classifai-provider-field hidden provider-scope-' . static::ID,
+			]
+		);
+
+		add_settings_field(
+			static::ID . '_deployment',
+			esc_html__( 'Deployment name', 'classifai' ),
+			[ $this->feature_instance, 'render_input' ],
+			$this->feature_instance->get_option_name(),
+			$this->feature_instance->get_option_name() . '_section',
+			[
+				'option_index'  => static::ID,
+				'label_for'     => 'deployment',
+				'input_type'    => 'text',
+				'default_value' => $settings['deployment'],
+				'description'   => __( 'Custom name you chose for your deployment when you deployed a model.', 'classifai' ),
+				'class'         => 'large-text classifai-provider-field hidden provider-scope-' . static::ID,
+			]
+		);
+
+		switch ( $this->feature_instance::ID ) {
+			case ContentResizing::ID:
+			case TitleGeneration::ID:
+				add_settings_field(
+					static::ID . '_number_of_suggestions',
+					esc_html__( 'Number of suggestions', 'classifai' ),
+					[ $this->feature_instance, 'render_input' ],
+					$this->feature_instance->get_option_name(),
+					$this->feature_instance->get_option_name() . '_section',
+					[
+						'option_index'  => static::ID,
+						'label_for'     => 'number_of_suggestions',
+						'input_type'    => 'number',
+						'min'           => 1,
+						'step'          => 1,
+						'default_value' => $settings['number_of_suggestions'],
+						'class'         => 'classifai-provider-field hidden provider-scope-' . static::ID,
+						'description'   => esc_html__( 'Number of suggestions that will be generated in one request.', 'classifai' ),
+					]
+				);
+				break;
+		}
+
+		do_action( 'classifai_' . static::ID . '_render_provider_fields', $this );
+	}
+
+	/**
+	 * Returns the default settings for this provider.
+	 *
+	 * @return array
+	 */
+	public function get_default_provider_settings(): array {
+		$common_settings = [
+			'endpoint_url'  => '',
+			'api_key'       => '',
+			'deployment'    => '',
+			'authenticated' => false,
+		];
+
+		/**
+		 * Default values for feature specific settings.
+		 */
+		switch ( $this->feature_instance::ID ) {
+			case ContentResizing::ID:
+			case TitleGeneration::ID:
+				$common_settings['number_of_suggestions'] = 1;
+				break;
+		}
+
+		return $common_settings;
+	}
+
+	/**
+	 * Sanitize the settings for this provider.
+	 *
+	 * @param array $new_settings The settings array.
+	 * @return array
+	 */
+	public function sanitize_settings( array $new_settings ): array {
+		$settings = $this->feature_instance->get_settings();
+
+		if (
+			! empty( $new_settings[ static::ID ]['endpoint_url'] ) &&
+			! empty( $new_settings[ static::ID ]['api_key'] ) &&
+			! empty( $new_settings[ static::ID ]['deployment'] )
+		) {
+			$new_settings[ static::ID ]['authenticated'] = $settings[ static::ID ]['authenticated'];
+			$new_settings[ static::ID ]['endpoint_url']  = esc_url_raw( $new_settings[ static::ID ]['endpoint_url'] ?? $settings[ static::ID ]['endpoint_url'] );
+			$new_settings[ static::ID ]['api_key']       = sanitize_text_field( $new_settings[ static::ID ]['api_key'] ?? $settings[ static::ID ]['api_key'] );
+			$new_settings[ static::ID ]['deployment']    = sanitize_text_field( $new_settings[ static::ID ]['deployment'] ?? $settings[ static::ID ]['deployment'] );
+
+			$is_authenticated   = $new_settings[ static::ID ]['authenticated'];
+			$is_endpoint_same   = $new_settings[ static::ID ]['endpoint_url'] === $settings[ static::ID ]['endpoint_url'];
+			$is_api_key_same    = $new_settings[ static::ID ]['api_key'] === $settings[ static::ID ]['api_key'];
+			$is_deployment_same = $new_settings[ static::ID ]['deployment'] === $settings[ static::ID ]['deployment'];
+
+			if ( ! ( $is_authenticated && $is_endpoint_same && $is_api_key_same && $is_deployment_same ) ) {
+				$auth_check = $this->authenticate_credentials(
+					$new_settings[ static::ID ]['endpoint_url'],
+					$new_settings[ static::ID ]['api_key'],
+					$new_settings[ static::ID ]['deployment']
+				);
+
+				if ( is_wp_error( $auth_check ) ) {
+					$new_settings[ static::ID ]['authenticated'] = false;
+					$error_message                               = $auth_check->get_error_message();
+
+					// Add an error message.
+					add_settings_error(
+						'api_key',
+						'classifai-auth',
+						$error_message,
+						'error'
+					);
+				} else {
+					$new_settings[ static::ID ]['authenticated'] = true;
+				}
+			}
+		} else {
+			$new_settings[ static::ID ]['endpoint_url'] = $settings[ static::ID ]['endpoint_url'];
+			$new_settings[ static::ID ]['api_key']      = $settings[ static::ID ]['api_key'];
+			$new_settings[ static::ID ]['deployment']   = $settings[ static::ID ]['deployment'];
+		}
+
+		switch ( $this->feature_instance::ID ) {
+			case ContentResizing::ID:
+			case TitleGeneration::ID:
+				$new_settings[ static::ID ]['number_of_suggestions'] = sanitize_number_of_responses_field( 'number_of_suggestions', $new_settings[ static::ID ], $settings[ static::ID ] );
+				break;
+		}
+
+		return $new_settings;
+	}
+
+	/**
+	 * Build and return the API endpoint based on settings.
+	 *
+	 * @param \Classifai\Features\Feature $feature Feature instance
+	 * @return string
+	 */
+	protected function prep_api_url( \Classifai\Features\Feature $feature = null ): string {
+		$settings   = $feature->get_settings( static::ID );
+		$endpoint   = $settings['endpoint_url'] ?? '';
+		$deployment = $settings['deployment'] ?? '';
+
+		if ( ! $endpoint ) {
+			return '';
+		}
+
+		if (
+			( $feature instanceof ContentResizing ||
+			$feature instanceof ExcerptGeneration ||
+			$feature instanceof TitleGeneration ) &&
+			$deployment
+		) {
+			$endpoint = trailingslashit( $endpoint ) . str_replace( '{deployment-id}', $deployment, $this->chat_completion_url );
+			$endpoint = add_query_arg( 'api-version', $this->chat_completion_api_version, $endpoint );
+		}
+
+		return $endpoint;
+	}
+
+	/**
+	 * Authenticates our credentials.
+	 *
+	 * @param string $url Endpoint URL.
+	 * @param string $api_key Api Key.
+	 * @param string $deployment Deployment name.
+	 * @return bool|WP_Error
+	 */
+	protected function authenticate_credentials( string $url, string $api_key, string $deployment ) {
+		$rtn = false;
+
+		// This does basically the same thing that prep_api_url does but when running authentication,
+		// we don't have settings saved yet, which prep_api_url needs.
+		$endpoint = trailingslashit( $url ) . str_replace( '{deployment-id}', $deployment, $this->chat_completion_url );
+		$endpoint = add_query_arg( 'api-version', $this->completion_api_version, $endpoint );
+
+		$request = wp_remote_post(
+			$endpoint,
+			[
+				'headers' => [
+					'api-key'      => $api_key,
+					'Content-Type' => 'application/json',
+				],
+				'body'    => wp_json_encode(
+					[
+						'prompt'     => 'Once upon a time',
+						'max_tokens' => 5,
+					]
+				),
+			]
+		);
+
+		if ( ! is_wp_error( $request ) ) {
+			$response = json_decode( wp_remote_retrieve_body( $request ) );
+			if ( ! empty( $response->error ) ) {
+				$rtn = new WP_Error( 'auth', $response->error->message );
+			} else {
+				$rtn = true;
+			}
+		}
+
+		return $rtn;
+	}
+
+	/**
+	 * Common entry point for all REST endpoints for this provider.
+	 *
+	 * @param int    $post_id The Post ID we're processing.
+	 * @param string $route_to_call The route we are processing.
+	 * @param array  $args Optional arguments to pass to the route.
+	 * @return string|WP_Error
+	 */
+	public function rest_endpoint_callback( $post_id = 0, string $route_to_call = '', array $args = [] ) {
+		if ( ! $post_id || ! get_post( $post_id ) ) {
+			return new WP_Error( 'post_id_required', esc_html__( 'A valid post ID is required to generate titles.', 'classifai' ) );
+		}
+
+		$route_to_call = strtolower( $route_to_call );
+		$return        = '';
+
+		// Handle all of our routes.
+		switch ( $route_to_call ) {
+			case 'excerpt':
+				$return = $this->generate_excerpt( $post_id, $args );
+				break;
+			case 'title':
+				$return = $this->generate_titles( $post_id, $args );
+				break;
+			case 'resize_content':
+				$return = $this->resize_content( $post_id, $args );
+				break;
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Generate an excerpt.
+	 *
+	 * @param int   $post_id The Post ID we're processing
+	 * @param array $args    Arguments passed in.
+	 * @return string|WP_Error
+	 */
+	public function generate_excerpt( int $post_id = 0, array $args = [] ) {
+		if ( ! $post_id || ! get_post( $post_id ) ) {
+			return new WP_Error( 'post_id_required', esc_html__( 'A valid post ID is required to generate an excerpt.', 'classifai' ) );
+		}
+
+		$feature  = new ExcerptGeneration();
+		$settings = $feature->get_settings();
+		$args     = wp_parse_args(
+			array_filter( $args ),
+			[
+				'content' => '',
+				'title'   => get_the_title( $post_id ),
+			]
+		);
+
+		// These checks (and the one above) happen in the REST permission_callback,
+		// but we run them again here in case this method is called directly.
+		if ( empty( $settings ) || ( isset( $settings[ static::ID ]['authenticated'] ) && false === $settings[ static::ID ]['authenticated'] ) || ( ! $feature->is_feature_enabled() && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) ) {
+			return new WP_Error( 'not_enabled', esc_html__( 'Excerpt generation is disabled or authentication failed. Please check your settings.', 'classifai' ) );
+		}
+
+		$excerpt_length = absint( $settings['length'] ?? 55 );
+		$excerpt_prompt = esc_textarea( get_default_prompt( $settings['generate_excerpt_prompt'] ) ?? $feature->prompt );
+
+		// Replace our variables in the prompt.
+		$prompt_search  = array( '{{WORDS}}', '{{TITLE}}' );
+		$prompt_replace = array( $excerpt_length, $args['title'] );
+		$prompt         = str_replace( $prompt_search, $prompt_replace, $excerpt_prompt );
+
+		/**
+		 * Filter the prompt we will send to Azure OpenAI.
+		 *
+		 * @since 3.0.0
+		 * @hook classifai_azure_openai_excerpt_prompt
+		 *
+		 * @param {string} $prompt Prompt we are sending. Gets added before post content.
+		 * @param {int} $post_id ID of post we are summarizing.
+		 * @param {int} $excerpt_length Length of final excerpt.
+		 *
+		 * @return {string} Prompt.
+		 */
+		$prompt = apply_filters( 'classifai_azure_openai_excerpt_prompt', $prompt, $post_id, $excerpt_length );
+
+		/**
+		 * Filter the request body before sending to Azure OpenAI.
+		 *
+		 * @since 3.0.0
+		 * @hook classifai_azure_openai_excerpt_request_body
+		 *
+		 * @param {array} $body Request body that will be sent.
+		 * @param {int} $post_id ID of post we are summarizing.
+		 *
+		 * @return {array} Request body.
+		 */
+		$body = apply_filters(
+			'classifai_azure_openai_excerpt_request_body',
+			[
+				'messages'    => [
+					[
+						'role'    => 'system',
+						'content' => 'You will be provided with content delimited by triple quotes. ' . $prompt,
+					],
+					[
+						'role'    => 'user',
+						'content' => '"""' . $this->get_content( $post_id, $excerpt_length, false, $args['content'] ) . '"""',
+					],
+				],
+				'temperature' => 0.9,
+			],
+			$post_id
+		);
+
+		// Make our API request.
+		$response = wp_remote_post(
+			$this->prep_api_url( $feature ),
+			[
+				'headers' => [
+					'api-key'      => $settings[ static::ID ]['api_key'],
+					'Content-Type' => 'application/json',
+				],
+				'body'    => wp_json_encode( $body ),
+			]
+		);
+		$response = $this->get_result( $response );
+
+		set_transient( 'classifai_azure_openai_excerpt_generation_latest_response', $response, DAY_IN_SECONDS * 30 );
+
+		// Extract out the text response, if it exists.
+		if ( ! is_wp_error( $response ) && ! empty( $response['choices'] ) ) {
+			foreach ( $response['choices'] as $choice ) {
+				if ( isset( $choice['message'], $choice['message']['content'] ) ) {
+					// ChatGPT often adds quotes to strings, so remove those as well as extra spaces.
+					$response = sanitize_text_field( trim( $choice['message']['content'], ' "\'' ) );
+				}
+			}
+		}
+
+		return $response;
+	}
+
+	/**
+	 * Generate titles using Azure OpenAI.
+	 *
+	 * @param int   $post_id The Post Id we're processing
+	 * @param array $args Arguments passed in.
+	 * @return string|WP_Error
+	 */
+	public function generate_titles( int $post_id = 0, array $args = [] ) {
+		if ( ! $post_id || ! get_post( $post_id ) ) {
+			return new WP_Error( 'post_id_required', esc_html__( 'Post ID is required to generate titles.', 'classifai' ) );
+		}
+
+		$feature  = new TitleGeneration();
+		$settings = $feature->get_settings();
+		$args     = wp_parse_args(
+			array_filter( $args ),
+			[
+				'num'     => $settings[ static::ID ]['number_of_suggestions'] ?? 1,
+				'content' => '',
+			]
+		);
+
+		// These checks happen in the REST permission_callback,
+		// but we run them again here in case this method is called directly.
+		if ( empty( $settings ) || ( isset( $settings[ static::ID ]['authenticated'] ) && false === $settings[ static::ID ]['authenticated'] ) || ! $feature->is_feature_enabled() ) {
+			return new WP_Error( 'not_enabled', esc_html__( 'Title generation is disabled or authentication failed. Please check your settings.', 'classifai' ) );
+		}
+
+		$prompt = esc_textarea( get_default_prompt( $settings['generate_title_prompt'] ) ?? $feature->prompt );
+
+		/**
+		 * Filter the prompt we will send to Azure OpenAI.
+		 *
+		 * @since 2.2.0
+		 * @hook classifai_azure_openai_title_prompt
+		 *
+		 * @param {string} $prompt Prompt we are sending. Gets added before post content.
+		 * @param {int} $post_id ID of post we are summarizing.
+		 * @param {array} $args Arguments passed to endpoint.
+		 *
+		 * @return {string} Prompt.
+		 */
+		$prompt = apply_filters( 'classifai_azure_openai_title_prompt', $prompt, $post_id, $args );
+
+		/**
+		 * Filter the request body before sending to Azure OpenAI.
+		 *
+		 * @since 2.2.0
+		 * @hook classifai_azure_openai_title_request_body
+		 *
+		 * @param {array} $body Request body that will be sent.
+		 * @param {int} $post_id ID of post we are summarizing.
+		 *
+		 * @return {array} Request body.
+		 */
+		$body = apply_filters(
+			'classifai_azure_openai_title_request_body',
+			[
+				'messages'    => [
+					[
+						'role'    => 'system',
+						'content' => 'You will be provided with content delimited by triple quotes. ' . $prompt,
+					],
+					[
+						'role'    => 'user',
+						'content' => '"""' . $this->get_content( $post_id, absint( $args['num'] ) * 15, false, $args['content'] ) . '"""',
+					],
+				],
+				'temperature' => 0.9,
+				'n'           => absint( $args['num'] ),
+			],
+			$post_id
+		);
+
+		// Make our API request.
+		$response = wp_remote_post(
+			$this->prep_api_url( $feature ),
+			[
+				'headers' => [
+					'api-key'      => $settings[ static::ID ]['api_key'],
+					'Content-Type' => 'application/json',
+				],
+				'body'    => wp_json_encode( $body ),
+			]
+		);
+		$response = $this->get_result( $response );
+
+		set_transient( 'classifai_azure_openai_title_generation_latest_response', $response, DAY_IN_SECONDS * 30 );
+
+		if ( is_wp_error( $response ) ) {
+			return $response;
+		}
+
+		if ( empty( $response['choices'] ) ) {
+			return new WP_Error( 'no_choices', esc_html__( 'No choices were returned from Azure OpenAI.', 'classifai' ) );
+		}
+
+		// Extract out the text response.
+		$return = [];
+		foreach ( $response['choices'] as $choice ) {
+			if ( isset( $choice['message'], $choice['message']['content'] ) ) {
+				// ChatGPT often adds quotes to strings, so remove those as well as extra spaces.
+				$return[] = sanitize_text_field( trim( $choice['message']['content'], ' "\'' ) );
+			}
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Resizes content.
+	 *
+	 * @param int   $post_id The Post Id we're processing
+	 * @param array $args Arguments passed in.
+	 * @return string|WP_Error
+	 */
+	public function resize_content( int $post_id, array $args = array() ) {
+		if ( ! $post_id || ! get_post( $post_id ) ) {
+			return new WP_Error( 'post_id_required', esc_html__( 'Post ID is required to resize content.', 'classifai' ) );
+		}
+
+		$feature  = new ContentResizing();
+		$settings = $feature->get_settings();
+
+		$args = wp_parse_args(
+			array_filter( $args ),
+			[
+				'num' => $settings[ static::ID ]['number_of_suggestions'] ?? 1,
+			]
+		);
+
+		if ( 'shrink' === $args['resize_type'] ) {
+			$prompt = esc_textarea( get_default_prompt( $settings['condense_text_prompt'] ) ?? $feature->condense_prompt );
+		} else {
+			$prompt = esc_textarea( get_default_prompt( $settings['expand_text_prompt'] ) ?? $feature->expand_prompt );
+		}
+
+		/**
+		 * Filter the resize prompt we will send to Azure OpenAI.
+		 *
+		 * @since 2.3.0
+		 * @hook classifai_azure_openai_' . $args['resize_type'] . '_content_prompt
+		 *
+		 * @param {string} $prompt Resize prompt we are sending. Gets added as a system prompt.
+		 * @param {int} $post_id ID of post.
+		 * @param {array} $args Arguments passed to endpoint.
+		 *
+		 * @return {string} Prompt.
+		 */
+		$prompt = apply_filters( 'classifai_azure_openai_' . $args['resize_type'] . '_content_prompt', $prompt, $post_id, $args );
+
+		/**
+		 * Filter the resize request body before sending to Azure OpenAI.
+		 *
+		 * @since 2.3.0
+		 * @hook classifai_azure_openai_resize_content_request_body
+		 *
+		 * @param {array} $body Request body that will be sent.
+		 * @param {int}   $post_id ID of post.
+		 *
+		 * @return {array} Request body.
+		 */
+		$body = apply_filters(
+			'classifai_azure_openai_resize_content_request_body',
+			[
+				'messages'    => [
+					[
+						'role'    => 'system',
+						'content' => 'You will be provided with content delimited by triple quotes. ' . $prompt,
+					],
+					[
+						'role'    => 'user',
+						'content' => '"""' . esc_html( $args['content'] ) . '"""',
+					],
+				],
+				'temperature' => 0.9,
+				'n'           => absint( $args['num'] ),
+			],
+			$post_id
+		);
+
+		// Make our API request.
+		$response = wp_remote_post(
+			$this->prep_api_url( $feature ),
+			[
+				'headers' => [
+					'api-key'      => $settings[ static::ID ]['api_key'],
+					'Content-Type' => 'application/json',
+				],
+				'body'    => wp_json_encode( $body ),
+			]
+		);
+		$response = $this->get_result( $response );
+
+		set_transient( 'classifai_azure_openai_content_resizing_latest_response', $response, DAY_IN_SECONDS * 30 );
+
+		if ( is_wp_error( $response ) ) {
+			return $response;
+		}
+
+		if ( empty( $response['choices'] ) ) {
+			return new WP_Error( 'no_choices', esc_html__( 'No choices were returned from Azure OpenAI.', 'classifai' ) );
+		}
+
+		// Extract out the text response.
+		$return = [];
+		foreach ( $response['choices'] as $choice ) {
+			if ( isset( $choice['message'], $choice['message']['content'] ) ) {
+				// ChatGPT often adds quotes to strings, so remove those as well as extra spaces.
+				$return[] = sanitize_text_field( trim( $choice['message']['content'], ' "\'' ) );
+			}
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Get our content.
+	 *
+	 * We don't trim content here as we don't know for sure which model
+	 * someone is using.
+	 *
+	 * @param int    $post_id Post ID to get content from.
+	 * @param int    $return_length Word length of returned content.
+	 * @param bool   $use_title Whether to use the title or not.
+	 * @param string $post_content The post content.
+	 * @return string
+	 */
+	public function get_content( int $post_id = 0, int $return_length = 0, bool $use_title = true, string $post_content = '' ): string {
+		$normalizer = new Normalizer();
+
+		if ( empty( $post_content ) ) {
+			$post         = get_post( $post_id );
+			$post_content = apply_filters( 'the_content', $post->post_content );
+		}
+
+		$post_content = preg_replace( '#\[.+\](.+)\[/.+\]#', '$1', $post_content );
+
+		// Add the title to the content, if needed, and normalize things.
+		if ( $use_title ) {
+			$content = $normalizer->normalize( $post_id, $post_content );
+		} else {
+			$content = $normalizer->normalize_content( $post_content, '', $post_id );
+		}
+
+		/**
+		 * Filter content that will get sent to Azure OpenAI.
+		 *
+		 * @since 3.0.0
+		 * @hook classifai_azure_openai_content
+		 *
+		 * @param {string} $content Content that will be sent.
+		 * @param {int} $post_id ID of post we are summarizing.
+		 *
+		 * @return {string} Content.
+		 */
+		return apply_filters( 'classifai_azure_openai_content', $content, $post_id );
+	}
+
+	/**
+	 * Get results from the response.
+	 *
+	 * @param object $response The API response.
+	 * @return array|WP_Error
+	 */
+	public function get_result( $response ) {
+		if ( is_wp_error( $response ) ) {
+			return $response;
+		}
+
+		$body = wp_remote_retrieve_body( $response );
+		$code = wp_remote_retrieve_response_code( $response );
+		$json = json_decode( $body, true );
+
+		if ( json_last_error() === JSON_ERROR_NONE ) {
+			if ( empty( $json['error'] ) ) {
+				return $json;
+			} else {
+				$message = $json['error']['message'] ?? esc_html__( 'An error occured', 'classifai' );
+				return new WP_Error( $code, $message );
+			}
+		} elseif ( ! empty( wp_remote_retrieve_response_message( $response ) ) ) {
+			return new WP_Error( $code, wp_remote_retrieve_response_message( $response ) );
+		} else {
+			return new WP_Error( 'Invalid JSON: ' . json_last_error_msg(), $body );
+		}
+	}
+
+	/**
+	 * Returns the debug information for the provider settings.
+	 *
+	 * @return array
+	 */
+	public function get_debug_information(): array {
+		$settings          = $this->feature_instance->get_settings();
+		$provider_settings = $settings[ static::ID ];
+		$debug_info        = [];
+
+		if ( $this->feature_instance instanceof TitleGeneration ) {
+			$debug_info[ __( 'No. of titles', 'classifai' ) ]         = $provider_settings['number_of_suggestions'] ?? 1;
+			$debug_info[ __( 'Generate title prompt', 'classifai' ) ] = wp_json_encode( $settings['generate_title_prompt'] ?? [] );
+			$debug_info[ __( 'Latest response', 'classifai' ) ]       = $this->get_formatted_latest_response( get_transient( 'classifai_azure_openai_title_generation_latest_response' ) );
+		} elseif ( $this->feature_instance instanceof ExcerptGeneration ) {
+			$debug_info[ __( 'Excerpt length', 'classifai' ) ]          = $settings['length'] ?? 55;
+			$debug_info[ __( 'Generate excerpt prompt', 'classifai' ) ] = wp_json_encode( $settings['generate_excerpt_prompt'] ?? [] );
+			$debug_info[ __( 'Latest response', 'classifai' ) ]         = $this->get_formatted_latest_response( get_transient( 'classifai_azure_openai_excerpt_generation_latest_response' ) );
+		} elseif ( $this->feature_instance instanceof ContentResizing ) {
+			$debug_info[ __( 'No. of suggestions', 'classifai' ) ]   = $provider_settings['number_of_suggestions'] ?? 1;
+			$debug_info[ __( 'Expand text prompt', 'classifai' ) ]   = wp_json_encode( $settings['expand_text_prompt'] ?? [] );
+			$debug_info[ __( 'Condense text prompt', 'classifai' ) ] = wp_json_encode( $settings['condense_text_prompt'] ?? [] );
+			$debug_info[ __( 'Latest response', 'classifai' ) ]      = $this->get_formatted_latest_response( get_transient( 'classifai_azure_openai_content_resizing_latest_response' ) );
+		}
+
+		return apply_filters(
+			'classifai_' . self::ID . '_debug_information',
+			$debug_info,
+			$settings,
+			$this->feature_instance
+		);
+	}
+}
diff --git a/includes/Classifai/Providers/GoogleAI/GeminiAPI.php b/includes/Classifai/Providers/GoogleAI/GeminiAPI.php
index d55981a86..f6f6c3923 100644
--- a/includes/Classifai/Providers/GoogleAI/GeminiAPI.php
+++ b/includes/Classifai/Providers/GoogleAI/GeminiAPI.php
@@ -575,9 +575,8 @@ public function get_content( int $post_id = 0, bool $use_title = true, string $p
 	 * @return array
 	 */
 	public function get_debug_information(): array {
-		$settings          = $this->feature_instance->get_settings();
-		$provider_settings = $settings[ static::ID ];
-		$debug_info        = [];
+		$settings   = $this->feature_instance->get_settings();
+		$debug_info = [];
 
 		if ( $this->feature_instance instanceof TitleGeneration ) {
 			$debug_info[ __( 'No. of titles', 'classifai' ) ]         = 1;
diff --git a/includes/Classifai/Services/LanguageProcessing.php b/includes/Classifai/Services/LanguageProcessing.php
index 6b36787aa..66baadae3 100644
--- a/includes/Classifai/Services/LanguageProcessing.php
+++ b/includes/Classifai/Services/LanguageProcessing.php
@@ -44,6 +44,7 @@ public static function get_service_providers(): array {
 				'Classifai\Providers\OpenAI\Whisper',
 				'Classifai\Providers\Watson\NLU',
 				'Classifai\Providers\GoogleAI\GeminiAPI',
+				'Classifai\Providers\Azure\OpenAI',
 			]
 		);
 	}
diff --git a/readme.txt b/readme.txt
index cc04ea699..685c1d7ac 100644
--- a/readme.txt
+++ b/readme.txt
@@ -12,15 +12,15 @@ Supercharge WordPress Content Workflows and Engagement with Artificial Intellige
 
 == Description ==
 
-Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Microsoft Azure AI](https://azure.microsoft.com/en-us/overview/ai-platform/), and [IBM Watson](https://www.ibm.com/watson) to augment your WordPress-powered websites.  Publish content faster while improving SEO performance and increasing audience engagement.  ClassifAI integrates Artificial Intelligence and Machine Learning technologies to lighten your workload and eliminate tedious tasks, giving you more time to create original content that matters.
+Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Microsoft Azure AI](https://azure.microsoft.com/en-us/overview/ai-platform/), [Google Gemini](https://ai.google.dev/) and [IBM Watson](https://www.ibm.com/watson) to augment your WordPress-powered websites.  Publish content faster while improving SEO performance and increasing audience engagement.  ClassifAI integrates Artificial Intelligence and Machine Learning technologies to lighten your workload and eliminate tedious tasks, giving you more time to create original content that matters.
 
 *You can learn more about ClassifAI's features at [ClassifAIPlugin.com](https://classifaiplugin.com/) and documentation at the [ClassifAI documentation site](https://10up.github.io/classifai/).*
 
 **Features**
 
-* Generate a summary of post content and store it as an excerpt using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat) and [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
-* Generate titles from post content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat) and [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
-* Expand or condense text content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat) and [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
+* Generate a summary of post content and store it as an excerpt using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
+* Generate titles from post content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
+* Expand or condense text content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
 * Generate new images on demand to use in-content or as a featured image using [OpenAI's DALL·E API](https://platform.openai.com/docs/guides/images)
 * Generate transcripts of audio files using [OpenAI's Whisper API](https://platform.openai.com/docs/guides/speech-to-text)
 * Convert text content into audio and output a "read-to-me" feature on the front-end to play this audio using [Microsoft Azure's Text to Speech API](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/text-to-speech)
@@ -35,6 +35,7 @@ Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Micro
 * To utilize the NLU Language Processing functionality, you will need an active [IBM Watson](https://cloud.ibm.com/registration) account.
 * To utilize the ChatGPT, Embeddings, or Whisper Language Processing functionality or DALL·E Image Processing functionality, you will need an active [OpenAI](https://platform.openai.com/signup) account.
 * To utilize the Azure AI Vision Image Processing functionality or Text to Speech Language Processing functionality, you will need an active [Microsoft Azure](https://signup.azure.com/signup) account.
+* To utilize the Azure OpenAI Language Processing functionality, you will need an active [Microsoft Azure](https://signup.azure.com/signup) account and you will need to [apply](https://customervoice.microsoft.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR7en2Ais5pxKtso_Pz4b1_xUNTZBNzRKNlVQSFhZMU9aV09EVzYxWFdORCQlQCN0PWcu) for OpenAI access.
 * To utilize the Google Gemini Language Processing functionality, you will need an active [Google Gemini](https://ai.google.dev/tutorials/setup) account.
 
 == Upgrade Notice ==
diff --git a/tests/cypress/integration/language-processing/excerpt-generation-azure-openai.test.js b/tests/cypress/integration/language-processing/excerpt-generation-azure-openai.test.js
new file mode 100644
index 000000000..c693b8fd8
--- /dev/null
+++ b/tests/cypress/integration/language-processing/excerpt-generation-azure-openai.test.js
@@ -0,0 +1,128 @@
+import { getChatGPTData } from '../../plugins/functions';
+
+describe( '[Language processing] Excerpt Generation Tests', () => {
+	before( () => {
+		cy.login();
+		cy.visit(
+			'/wp-admin/tools.php?page=classifai&tab=language_processing&feature=feature_excerpt_generation'
+		);
+		cy.get( '#status' ).check();
+		cy.get(
+			'#classifai_feature_excerpt_generation_post_types_post'
+		).check();
+		cy.get( '#submit' ).click();
+		cy.optInAllFeatures();
+		cy.disableClassicEditor();
+	} );
+
+	beforeEach( () => {
+		cy.login();
+	} );
+
+	it( 'Can save Azure OpenAI "Language Processing" settings', () => {
+		cy.visit(
+			'/wp-admin/tools.php?page=classifai&tab=language_processing&feature=feature_excerpt_generation'
+		);
+
+		cy.get( '#provider' ).select( 'azure_openai' );
+		cy.get(
+			'input[name="classifai_feature_excerpt_generation[azure_openai][endpoint_url]"]'
+		)
+			.clear()
+			.type( 'https://e2e-test-azure-openai.test/' );
+		cy.get(
+			'input[name="classifai_feature_excerpt_generation[azure_openai][api_key]"]'
+		)
+			.clear()
+			.type( 'password' );
+		cy.get(
+			'input[name="classifai_feature_excerpt_generation[azure_openai][deployment]"]'
+		)
+			.clear()
+			.type( 'test' );
+
+		cy.get( '#status' ).check();
+		cy.get(
+			'#classifai_feature_excerpt_generation_roles_administrator'
+		).check();
+		cy.get( '#length' ).clear().type( 35 );
+		cy.get( '#submit' ).click();
+	} );
+
+	it( 'Can see the generate excerpt button in a post', () => {
+		cy.visit( '/wp-admin/plugins.php' );
+		cy.disableClassicEditor();
+
+		const data = getChatGPTData();
+
+		// Create test post.
+		cy.createPost( {
+			title: 'Test Azure OpenAI post',
+			content: 'Test content',
+		} );
+
+		// Close post publish panel.
+		const closePanelSelector = 'button[aria-label="Close panel"]';
+		cy.get( 'body' ).then( ( $body ) => {
+			if ( $body.find( closePanelSelector ).length > 0 ) {
+				cy.get( closePanelSelector ).click();
+			}
+		} );
+
+		// Open post settings sidebar.
+		cy.openDocumentSettingsSidebar();
+
+		// Find and open the excerpt panel.
+		const panelButtonSelector = `.components-panel__body .components-panel__body-title button:contains("Excerpt")`;
+
+		cy.get( panelButtonSelector ).then( ( $panelButton ) => {
+			// Find the panel container.
+			const $panel = $panelButton.parents( '.components-panel__body' );
+
+			// Open panel.
+			if ( ! $panel.hasClass( 'is-opened' ) ) {
+				cy.wrap( $panelButton ).click();
+			}
+
+			// Verify button exists.
+			cy.wrap( $panel )
+				.find( '.editor-post-excerpt button' )
+				.should( 'exist' );
+
+			// Click on button and verify data loads in.
+			cy.wrap( $panel ).find( '.editor-post-excerpt button' ).click();
+			cy.wrap( $panel ).find( 'textarea' ).should( 'have.value', data );
+		} );
+	} );
+
+	it( 'Can see the generate excerpt button in a post (Classic Editor)', () => {
+		cy.enableClassicEditor();
+
+		cy.visit(
+			'/wp-admin/tools.php?page=classifai&tab=language_processing&feature=feature_excerpt_generation'
+		);
+		cy.get( '#status' ).check();
+		cy.get( '#submit' ).click();
+
+		const data = getChatGPTData();
+
+		cy.createClassicPost( {
+			title: 'Excerpt test classic',
+			content: 'Test GPT content.',
+			postType: 'post',
+		} );
+
+		// Ensure excerpt metabox is shown.
+		cy.get( '#show-settings-link' ).click();
+		cy.get( '#postexcerpt-hide' ).check( { force: true } );
+
+		// Verify button exists.
+		cy.get( '#classifai-openai__excerpt-generate-btn' ).should( 'exist' );
+
+		// Click on button and verify data loads in.
+		cy.get( '#classifai-openai__excerpt-generate-btn' ).click();
+		cy.get( '#excerpt' ).should( 'have.value', data );
+
+		cy.disableClassicEditor();
+	} );
+} );
diff --git a/tests/cypress/integration/language-processing/resize_content-azure-openai.test.js b/tests/cypress/integration/language-processing/resize_content-azure-openai.test.js
new file mode 100644
index 000000000..46e1d9735
--- /dev/null
+++ b/tests/cypress/integration/language-processing/resize_content-azure-openai.test.js
@@ -0,0 +1,91 @@
+describe( '[Language processing] Resize Content Tests', () => {
+	before( () => {
+		cy.login();
+		cy.visit(
+			'/wp-admin/tools.php?page=classifai&tab=language_processing&feature=feature_content_resizing'
+		);
+		cy.get( '#status' ).check();
+		cy.get( '#provider' ).select( 'azure_openai' );
+		cy.get(
+			'input[name="classifai_feature_content_resizing[azure_openai][endpoint_url]"]'
+		)
+			.clear()
+			.type( 'https://e2e-test-azure-openai.test/' );
+		cy.get(
+			'input[name="classifai_feature_content_resizing[azure_openai][api_key]"]'
+		)
+			.clear()
+			.type( 'password' );
+		cy.get(
+			'input[name="classifai_feature_content_resizing[azure_openai][deployment]"]'
+		)
+			.clear()
+			.type( 'test' );
+		cy.get( '#submit' ).click();
+		cy.optInAllFeatures();
+		cy.disableClassicEditor();
+	} );
+
+	beforeEach( () => {
+		cy.login();
+	} );
+
+	it( 'Resize content feature can grow and shrink content', () => {
+		cy.visit(
+			'/wp-admin/tools.php?page=classifai&tab=language_processing&feature=feature_content_resizing'
+		);
+
+		cy.get( '#status' ).check();
+		cy.get(
+			'#classifai_feature_content_resizing_roles_administrator'
+		).check();
+		cy.get( '#submit' ).click();
+
+		cy.createPost( {
+			title: 'Resize content',
+			content: 'Hello, world.',
+		} );
+
+		cy.get( '.classifai-resize-content-btn' ).click();
+		cy.get( '.components-button' ).contains( 'Expand this text' ).click();
+		cy.get(
+			'.classifai-content-resize__result-table tbody tr:first .classifai-content-resize__grow-stat'
+		).should( 'contain.text', '+7 words' );
+		cy.get(
+			'.classifai-content-resize__result-table tbody tr:first .classifai-content-resize__grow-stat'
+		).should( 'contain.text', '+40 characters' );
+		cy.get(
+			'.classifai-content-resize__result-table tbody tr:first button'
+		).click();
+		cy.getBlockEditor()
+			.find( '[data-type="core/paragraph"]' )
+			.should(
+				'contain.text',
+				'Start with the basic building block of one narrative.'
+			);
+
+		cy.createPost( {
+			title: 'Resize content',
+			content:
+				'Start with the basic building block of one narrative to begin with the editorial process.',
+		} );
+
+		cy.get( '.classifai-resize-content-btn' ).click();
+		cy.get( '.components-button' ).contains( 'Condense this text' ).click();
+		cy.get(
+			'.classifai-content-resize__result-table tbody tr:first .classifai-content-resize__shrink-stat'
+		).should( 'contain.text', '-6 words' );
+		cy.get(
+			'.classifai-content-resize__result-table tbody tr:first .classifai-content-resize__shrink-stat'
+		).should( 'contain.text', '-36 characters' );
+		cy.get(
+			'.classifai-content-resize__result-table tbody tr:first button'
+		).click();
+		cy.getBlockEditor()
+			.find( '[data-type="core/paragraph"]' )
+			.should(
+				'contain.text',
+				'Start with the basic building block of one narrative.'
+			);
+	} );
+} );
diff --git a/tests/cypress/integration/language-processing/resize_content-googleai-gemini-api.test.js b/tests/cypress/integration/language-processing/resize_content-googleai-gemini-api.test.js
index daf7c22d3..4c21eda01 100644
--- a/tests/cypress/integration/language-processing/resize_content-googleai-gemini-api.test.js
+++ b/tests/cypress/integration/language-processing/resize_content-googleai-gemini-api.test.js
@@ -1,4 +1,4 @@
-describe( '[Language processing] Speech to Text Tests', () => {
+describe( '[Language processing] Resize Content Tests', () => {
 	before( () => {
 		cy.login();
 		cy.visit(
diff --git a/tests/cypress/integration/language-processing/resize_content-openapi-chatgpt.test.js b/tests/cypress/integration/language-processing/resize_content-openapi-chatgpt.test.js
index 136f3cfe5..9b8c17066 100644
--- a/tests/cypress/integration/language-processing/resize_content-openapi-chatgpt.test.js
+++ b/tests/cypress/integration/language-processing/resize_content-openapi-chatgpt.test.js
@@ -1,4 +1,4 @@
-describe( '[Language processing] Speech to Text Tests', () => {
+describe( '[Language processing] Resize Content Tests', () => {
 	before( () => {
 		cy.login();
 		cy.visit(
diff --git a/tests/cypress/integration/language-processing/title-generation-azure-openai.test.js b/tests/cypress/integration/language-processing/title-generation-azure-openai.test.js
new file mode 100644
index 000000000..477e20b12
--- /dev/null
+++ b/tests/cypress/integration/language-processing/title-generation-azure-openai.test.js
@@ -0,0 +1,137 @@
+import { getChatGPTData } from '../../plugins/functions';
+
+describe( '[Language processing] Title Generation Tests', () => {
+	before( () => {
+		cy.login();
+		cy.optInAllFeatures();
+		cy.disableClassicEditor();
+	} );
+
+	beforeEach( () => {
+		cy.login();
+	} );
+
+	it( 'Can save Azure OpenAI "Language Processing" title settings', () => {
+		cy.visit(
+			'/wp-admin/tools.php?page=classifai&tab=language_processing&feature=feature_title_generation'
+		);
+
+		cy.get( '#provider' ).select( 'azure_openai' );
+		cy.get(
+			'input[name="classifai_feature_title_generation[azure_openai][endpoint_url]"]'
+		)
+			.clear()
+			.type( 'https://e2e-test-azure-openai.test/' );
+		cy.get(
+			'input[name="classifai_feature_title_generation[azure_openai][api_key]"]'
+		)
+			.clear()
+			.type( 'password' );
+		cy.get(
+			'input[name="classifai_feature_title_generation[azure_openai][deployment]"]'
+		)
+			.clear()
+			.type( 'test' );
+
+		cy.get( '#status' ).check();
+		cy.get(
+			'#classifai_feature_title_generation_roles_administrator'
+		).check();
+		cy.get(
+			'input[name="classifai_feature_title_generation[azure_openai][number_of_suggestions]"]'
+		)
+			.clear()
+			.type( 1 );
+		cy.get( '#submit' ).click();
+	} );
+
+	it( 'Can see the generate titles button in a post', () => {
+		const data = getChatGPTData();
+
+		// Create test post.
+		cy.createPost( {
+			title: 'Test Azure OpenAI generate titles',
+			content: 'Test content',
+		} );
+
+		// Close post publish panel.
+		const closePanelSelector = 'button[aria-label="Close panel"]';
+		cy.get( 'body' ).then( ( $body ) => {
+			if ( $body.find( closePanelSelector ).length > 0 ) {
+				cy.get( closePanelSelector ).click();
+			}
+		} );
+
+		// Open post settings sidebar.
+		cy.openDocumentSettingsSidebar();
+
+		// Find and open the summary panel.
+		const panelButtonSelector = `.components-panel__body.edit-post-post-status .components-panel__body-title button`;
+
+		cy.get( panelButtonSelector ).then( ( $panelButton ) => {
+			// Find the panel container.
+			const $panel = $panelButton.parents( '.components-panel__body' );
+
+			// Open panel.
+			if ( ! $panel.hasClass( 'is-opened' ) ) {
+				cy.wrap( $panelButton ).click();
+			}
+
+			// Verify button exists.
+			cy.wrap( $panel )
+				.find( '.classifai-post-status button.title' )
+				.should( 'exist' );
+
+			// Click on button and verify modal shows.
+			cy.wrap( $panel )
+				.find( '.classifai-post-status button.title' )
+				.click();
+		} );
+
+		cy.get( '.title-modal' ).should( 'exist' );
+
+		// Click on button and verify data loads in.
+		cy.get( '.title-modal .classifai-title' )
+			.first()
+			.find( 'textarea' )
+			.should( 'have.value', data );
+		cy.get( '.title-modal .classifai-title' )
+			.first()
+			.find( 'button' )
+			.click();
+
+		cy.get( '.title-modal' ).should( 'not.exist' );
+		cy.getBlockEditor()
+			.find( '.editor-post-title__input' )
+			.should( ( $el ) => {
+				expect( $el.first() ).to.contain( data );
+			} );
+	} );
+
+	it( 'Can see the generate titles button in a post (Classic Editor)', () => {
+		cy.enableClassicEditor();
+
+		cy.visit(
+			'/wp-admin/tools.php?page=classifai&tab=language_processing&feature=feature_title_generation'
+		);
+		cy.get( '#status' ).check();
+		cy.get( '#submit' ).click();
+
+		const data = getChatGPTData();
+
+		cy.visit( '/wp-admin/post-new.php' );
+
+		cy.get( '#classifai-openai__title-generate-btn' ).click();
+		cy.get( '#classifai-openai__modal' ).should( 'be.visible' );
+		cy.get( '.classifai-openai__result-item' )
+			.first()
+			.find( 'textarea' )
+			.should( 'have.value', data );
+
+		cy.get( '.classifai-openai__select-title' ).first().click();
+		cy.get( '#classifai-openai__modal' ).should( 'not.be.visible' );
+		cy.get( '#title' ).should( 'have.value', data );
+
+		cy.disableClassicEditor();
+	} );
+} );
diff --git a/tests/test-plugin/e2e-test-plugin.php b/tests/test-plugin/e2e-test-plugin.php
index 450df3da1..c6ce7a891 100644
--- a/tests/test-plugin/e2e-test-plugin.php
+++ b/tests/test-plugin/e2e-test-plugin.php
@@ -23,7 +23,10 @@ function classifai_test_mock_http_requests( $preempt, $parsed_args, $url ) {
 		$response = file_get_contents( __DIR__ . '/models.json' );
 	} elseif ( strpos( $url, 'https://api.openai.com/v1/completions' ) !== false ) {
 		$response = file_get_contents( __DIR__ . '/chatgpt.json' );
-	} elseif ( strpos( $url, 'https://api.openai.com/v1/chat/completions' ) !== false ) {
+	} elseif (
+		strpos( $url, 'https://api.openai.com/v1/chat/completions' ) !== false ||
+		strpos( $url, 'https://e2e-test-azure-openai.test/openai/deployments' ) !== false
+	) {
 		$response  = file_get_contents( __DIR__ . '/chatgpt.json' );
 		$body_json = $parsed_args['body'] ?? false;