diff --git a/configs/samples/features.conf.sample b/configs/samples/features.conf.sample
index afdcf5ae4fe261123089f4fb12ffe8aa6b91118b..05b514e7b98d481ee95ac8857b4c294bcc900932 100644
--- a/configs/samples/features.conf.sample
+++ b/configs/samples/features.conf.sample
@@ -6,7 +6,11 @@
 
 [general]
 ;transferdigittimeout => 3      ; Number of seconds to wait between digits when transferring a call
-                                ; (default is 3 seconds)
+                                ; (default is 3 seconds). If the TRANSFER_EXTEN dialplan variable has been set
+                                ; on the channel of the user that is invoking the transfer feature, then
+                                ; this option is not used as the user is transferred directly to the extension
+                                ; specified by TRANSFER_EXTEN (the transfer context remains the context specified
+                                ; by TRANSFER_CONTEXT, if set, and otherwise the default context).
 ;xfersound = beep               ; to indicate an attended transfer is complete
 ;xferfailsound = beeperr        ; to indicate a failed transfer
 ;pickupexten = *8               ; Configure the pickup extension. (default is *8)
@@ -26,12 +30,13 @@
                                 ; By default, this is 2.
 ;transferdialattempts = 3       ; Number of times that a transferer may attempt to dial an extension before
                                 ; being kicked back to the original call.
+;transferannouncesound = beep   ; Sound to play to a transferer to indicate transfer process has begun. If empty, no sound will be played.
 ;transferretrysound = beep      ; Sound to play when a transferer fails to dial a valid extension.
 ;transferinvalidsound = beeperr ; Sound to play when a transferer fails to dial a valid extension and is out of retries.
 ;atxferabort = *1               ; cancel the attended transfer
 ;atxfercomplete = *2            ; complete the attended transfer, dropping out of the call
 ;atxferthreeway = *3            ; complete the attended transfer, but stay in the call. This will turn the call into a multi-party bridge
-;atxferswap = *4                ; swap to the other party. Once an attended transfer has begun, this options may be used multiple times
+;atxferswap = *4                ; swap to the other party. Once an attended transfer has begun, this option may be used multiple times
 
 ; Note that the DTMF features listed below only work when two channels have answered and are bridged together.
 ; They can not be used while the remote party is ringing or in progress. If you require this feature you can use
diff --git a/doc/CHANGES-staging/transfer.txt b/doc/CHANGES-staging/transfer.txt
new file mode 100644
index 0000000000000000000000000000000000000000..962272fcf78cc127fae937b0714570f3aaaf4105
--- /dev/null
+++ b/doc/CHANGES-staging/transfer.txt
@@ -0,0 +1,14 @@
+Subject: Transfer feature
+
+The following capabilities have been added to the
+transfer feature:
+
+- The transfer initiation announcement prompt can
+now be customized in features.conf.
+
+- The TRANSFER_EXTEN variable now can be set on the
+transferer's channel in order to allow the transfer
+function to automatically attempt to go to the extension
+contained in this variable, if it exists. The transfer
+context behavior is not changed (TRANSFER_CONTEXT is used
+if it exists; otherwise the default context is used).
diff --git a/include/asterisk/features_config.h b/include/asterisk/features_config.h
index 52533693b59688629beaf88711bda62def020230..9d626ee0b7c4eb9980c73de2a137a97f7b9a954c 100644
--- a/include/asterisk/features_config.h
+++ b/include/asterisk/features_config.h
@@ -72,6 +72,8 @@ struct ast_features_xfer_config {
 		AST_STRING_FIELD(transferretrysound);
 		/*! Sound played when an invalid extension is dialed, and the transferer is being returned to the call. */
 		AST_STRING_FIELD(transferinvalidsound);
+		/*! Sound to play to announce the transfer process has started. */
+		AST_STRING_FIELD_EXTENDED(transferannouncesound);
 	);
 	/*! Seconds allowed between digit presses when dialing transfer destination */
 	unsigned int transferdigittimeout;
diff --git a/main/bridge_basic.c b/main/bridge_basic.c
index df211957f6a7de2930c4a8292197cb98aad8a18e..c291fb202d030b1a6a8d0da5a897b659c6e3b7a5 100644
--- a/main/bridge_basic.c
+++ b/main/bridge_basic.c
@@ -1397,6 +1397,27 @@ static const char *get_transfer_context(struct ast_channel *transferer, const ch
 	return "default";
 }
 
+/*!
+ * \internal
+ * \brief Determine the transfer extension to use.
+ *
+ * \param transferer Channel initiating the transfer.
+ * \param extension User supplied extension if available.  May be NULL.
+ *
+ * \return The extension to use for the transfer.
+ */
+static const char *get_transfer_exten(struct ast_channel *transferer, const char *exten)
+{
+	if (!ast_strlen_zero(exten)) {
+		return exten;
+	}
+	exten = pbx_builtin_getvar_helper(transferer, "TRANSFER_EXTEN");
+	if (!ast_strlen_zero(exten)) {
+		return exten;
+	}
+	return ""; /* empty default, to get transfer extension from user now */
+}
+
 /*!
  * \brief Allocate and initialize attended transfer properties
  *
@@ -3162,10 +3183,25 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
 	int attempts = 0;
 	int max_attempts;
 	struct ast_features_xfer_config *xfer_cfg;
-	char *retry_sound;
-	char *invalid_sound;
+	char *announce_sound, *retry_sound, *invalid_sound;
+	const char *extenoverride;
 
 	ast_channel_lock(chan);
+	extenoverride = get_transfer_exten(chan, NULL);
+
+	if (!ast_strlen_zero(extenoverride)) {
+		int extenres = ast_exists_extension(chan, context, extenoverride, 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)) ? 1 : 0;
+		if (extenres) {
+			ast_copy_string(exten, extenoverride, exten_len);
+			ast_channel_unlock(chan);
+			ast_verb(3, "Transfering call to '%s@%s'", exten, context);
+			return 0;
+		}
+		ast_log(LOG_WARNING, "Override extension '%s' does not exist in context '%s'\n", extenoverride, context);
+		/* since we didn't get a valid extension from the channel, fall back and grab it from the user as usual now */
+	}
+
 	xfer_cfg = ast_get_chan_features_xfer_config(chan);
 	if (!xfer_cfg) {
 		ast_log(LOG_ERROR, "Channel %s: Unable to get transfer configuration\n",
@@ -3175,21 +3211,24 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
 	}
 	digit_timeout = xfer_cfg->transferdigittimeout * 1000;
 	max_attempts = xfer_cfg->transferdialattempts;
+	announce_sound = ast_strdupa(xfer_cfg->transferannouncesound);
 	retry_sound = ast_strdupa(xfer_cfg->transferretrysound);
 	invalid_sound = ast_strdupa(xfer_cfg->transferinvalidsound);
 	ao2_ref(xfer_cfg, -1);
 	ast_channel_unlock(chan);
 
 	/* Play the simple "transfer" prompt out and wait */
-	res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
-	ast_stopstream(chan);
-	if (res < 0) {
-		/* Hangup or error */
-		return -1;
-	}
-	if (res) {
-		/* Store the DTMF digit that interrupted playback of the file. */
-		exten[0] = res;
+	if (!ast_strlen_zero(announce_sound)) {
+		res = ast_stream_and_wait(chan, announce_sound, AST_DIGIT_ANY);
+		ast_stopstream(chan);
+		if (res < 0) {
+			/* Hangup or error */
+			return -1;
+		}
+		if (res) {
+			/* Store the DTMF digit that interrupted playback of the file. */
+			exten[0] = res;
+		}
 	}
 
 	/* Drop to dialtone so they can enter the extension they want to transfer to */
diff --git a/main/features_config.c b/main/features_config.c
index ac0135abe38c087d118aab0cd2a9335dda639f53..ba2f905003e65f0ad88c74a45dbef8ba671c9a0e 100644
--- a/main/features_config.c
+++ b/main/features_config.c
@@ -143,6 +143,9 @@
 				<configOption name="transferinvalidsound" default="privacy-incorrect">
 					<synopsis>Sound that is played when an incorrect extension is dialed and the transferer has no attempts remaining.</synopsis>
 				</configOption>
+				<configOption name="transferannouncesound" default="pbx-transfer">
+					<synopsis>Sound that is played to the transferer when a transfer is initiated. If empty, no sound will be played.</synopsis>
+				</configOption>
 			</configObject>
 			<configObject name="featuremap">
 				<synopsis>DTMF options that can be triggered during bridged calls</synopsis>
@@ -324,6 +327,7 @@
 					<enum name="transferdialattempts"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferdialattempts']/synopsis/text())" /></para></enum>
 					<enum name="transferretrysound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferretrysound']/synopsis/text())" /></para></enum>
 					<enum name="transferinvalidsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferinvalidsound']/synopsis/text())" /></para></enum>
+					<enum name="transferannouncesound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferannouncesound']/synopsis/text())" /></para></enum>
 				</enumlist>
 			</parameter>
 		</syntax>
@@ -387,6 +391,7 @@
 #define DEFAULT_TRANSFER_DIAL_ATTEMPTS              3
 #define DEFAULT_TRANSFER_RETRY_SOUND                "pbx-invalid"
 #define DEFAULT_TRANSFER_INVALID_SOUND              "privacy-incorrect"
+#define DEFAULT_TRANSFER_ANNOUNCE_SOUND             "pbx-transfer"
 
 /*! Default pickup options */
 #define DEFAULT_PICKUPEXTEN                         "*8"
@@ -910,6 +915,8 @@ static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
 		ast_string_field_set(xfer, transferretrysound, value);
 	} else if (!strcasecmp(name, "transferinvalidsound")) {
 		ast_string_field_set(xfer, transferinvalidsound, value);
+	} else if (!strcasecmp(name, "transferannouncesound")) {
+		ast_string_field_set(xfer, transferannouncesound, value);
 	} else {
 		/* Unrecognized option */
 		res = -1;
@@ -1801,6 +1808,8 @@ static int load_config(void)
 			DEFAULT_TRANSFER_RETRY_SOUND, xfer_handler, 0);
 	aco_option_register_custom(&cfg_info, "transferinvalidsound", ACO_EXACT, global_options,
 			DEFAULT_TRANSFER_INVALID_SOUND, xfer_handler, 0);
+	aco_option_register_custom(&cfg_info, "transferannouncesound", ACO_EXACT, global_options,
+			DEFAULT_TRANSFER_ANNOUNCE_SOUND, xfer_handler, 0);
 
 	aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
 			DEFAULT_PICKUPEXTEN, pickup_handler, 0);