diff --git a/apps/app_transfer.c b/apps/app_transfer.c
index 4fe3dcac0667ca02a7b8ba507fa962c128549f4d..d152340baaf15be7f8978af7ac5d9d0e09515fb8 100644
--- a/apps/app_transfer.c
+++ b/apps/app_transfer.c
@@ -69,6 +69,14 @@
 						Transfer unsupported by channel driver.
 					</value>
 				</variable>
+				<variable name="TRANSFERSTATUSPROTOCOL">
+					<value name="0">
+						No error.
+					</value>
+					<value name="3xx-6xx">
+						SIP example - Error result code.
+					</value>
+				</variable>
 			</variablelist>
 		</description>
 	</application>
@@ -85,6 +93,8 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
 	char *dest = NULL;
 	char *status;
 	char *parse;
+	int protocol = 0;
+	char status_protocol[20];
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(dest);
 	);
@@ -92,6 +102,8 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
 	if (ast_strlen_zero((char *)data)) {
 		ast_log(LOG_WARNING, "Transfer requires an argument ([Tech/]destination)\n");
 		pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
+		snprintf(status_protocol, sizeof(status_protocol), "%d", protocol);
+		pbx_builtin_setvar_helper(chan, "TRANSFERSTATUSPROTOCOL", status_protocol);
 		return 0;
 	} else
 		parse = ast_strdupa(data);
@@ -106,6 +118,8 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
 		/* Allow execution only if the Tech/destination agrees with the type of the channel */
 		if (strncasecmp(ast_channel_tech(chan)->type, tech, len)) {
 			pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
+			snprintf(status_protocol, sizeof(status_protocol), "%d", protocol);
+			pbx_builtin_setvar_helper(chan, "TRANSFERSTATUSPROTOCOL", status_protocol);
 			return 0;
 		}
 	}
@@ -113,10 +127,14 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
 	/* Check if the channel supports transfer before we try it */
 	if (!ast_channel_tech(chan)->transfer) {
 		pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "UNSUPPORTED");
+		snprintf(status_protocol, sizeof(status_protocol), "%d", protocol);
+		pbx_builtin_setvar_helper(chan, "TRANSFERSTATUSPROTOCOL", status_protocol);
 		return 0;
 	}
 
-	res = ast_transfer(chan, dest);
+	/* New transfer API returns a protocol code
+	   SIP example, 0 = success, 3xx-6xx are sip error codes for the REFER */
+	res = ast_transfer_protocol(chan, dest, &protocol);
 
 	if (res < 0) {
 		status = "FAILURE";
@@ -126,7 +144,11 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
 		res = 0;
 	}
 
+	snprintf(status_protocol, sizeof(status_protocol), "%d", protocol);
+	ast_debug(1, "ast_transfer channel %s TRANSFERSTATUS=%s, TRANSFERSTATUSPROTOCOL=%s\n",
+		  ast_channel_name(chan), status, status_protocol);
 	pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", status);
+	pbx_builtin_setvar_helper(chan, "TRANSFERSTATUSPROTOCOL", status_protocol);
 
 	return res;
 }
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 483cd3ef0094688283773244ea6ec43178d3ea92..5c194434685f8284240d979ae708f78afe44cb5b 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -1982,12 +1982,17 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
 			rdata = event->body.tsx_state.src.rdata;
 			msg = rdata->msg_info.msg;
 
-			if (!pjsip_method_cmp(&msg->line.req.method, pjsip_get_notify_method())) {
-				body = msg->body;
-				if (body && !pj_stricmp2(&body->content_type.type, "message")
-					&& !pj_stricmp2(&body->content_type.subtype, "sipfrag")) {
-					pjsip_parse_status_line((char *)body->data, body->len, &status_line);
+			if (msg->type == PJSIP_REQUEST_MSG) {
+				if (!pjsip_method_cmp(&msg->line.req.method, pjsip_get_notify_method())) {
+					body = msg->body;
+					if (body && !pj_stricmp2(&body->content_type.type, "message")
+						&& !pj_stricmp2(&body->content_type.subtype, "sipfrag")) {
+						pjsip_parse_status_line((char *)body->data, body->len, &status_line);
+					}
 				}
+			} else {
+				status_line.code = msg->line.status.code;
+				status_line.reason = msg->line.status.reason;
 			}
 		} else {
 			status_line.code = 500;
@@ -2000,12 +2005,16 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
 			res = -1;
 
 			/* If the subscription has terminated, return AST_TRANSFER_SUCCESS for 2XX.
-			 * Any other status code returns AST_TRANSFER_FAILED.
+			 * Return AST_TRANSFER_FAILED for any code < 200.
+			 * Otherwise, return the status code.
 			 * The subscription should not terminate for any code < 200,
 			 * but if it does, that constitutes a failure. */
-			if (status_line.code < 200 || status_line.code >= 300) {
+			if (status_line.code < 200) {
 				message = AST_TRANSFER_FAILED;
+			} else if (status_line.code >= 300) {
+				message = status_line.code;
 			}
+
 			/* If subscription not terminated and subscription is finished (status code >= 200)
 			 * terminate it */
 			if (!is_last) {
diff --git a/doc/CHANGES-staging/app_transferprotocol.txt b/doc/CHANGES-staging/app_transferprotocol.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5d3521bbd45addbc48ab8d9f40cf640b90c5ebca
--- /dev/null
+++ b/doc/CHANGES-staging/app_transferprotocol.txt
@@ -0,0 +1,6 @@
+Subject: chan_pjsip, app_transfer
+
+Added TRANSFERSTATUSPROTOCOL variable.  When transfer is performed,
+transfers can pass a protocol specific error code.
+Example, in SIP 3xx-6xx represent any SIP specific error received when
+performing a REFER.
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index baefeddaed7db70f5f4f33e94ab08b8577754197..40069b0178db246a7afcdbb30781e66922b5f61c 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -2640,6 +2640,18 @@ int ast_settimeout_full(struct ast_channel *c, unsigned int rate, int (*func)(co
  */
 int ast_transfer(struct ast_channel *chan, char *dest);
 
+/*!
+ * \brief Transfer a channel (if supported) receieve protocol result.
+ * \retval -1 on error
+ * \retval 0 if not supported
+ * \retval 1 if supported and requested
+ * \param chan current channel
+ * \param dest destination extension for transfer
+ * \param protocol specific error code in case of failure
+ * Example, sip 0 success, else sip error code
+ */
+int ast_transfer_protocol(struct ast_channel *chan, char *dest, int *protocol);
+
 /*!
  * \brief Inherits channel variable from parent to child channel
  * \param parent Parent channel
diff --git a/main/channel.c b/main/channel.c
index 9730ed08de318c250f28b118931b2d864a58403a..784ea28447a0e427122fc0cd179e7b7e581c840d 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -6474,9 +6474,31 @@ int ast_call(struct ast_channel *chan, const char *addr, int timeout)
 	\arg the manager interface
 */
 int ast_transfer(struct ast_channel *chan, char *dest)
+{
+	int protocol;
+	return ast_transfer_protocol(chan, dest, &protocol);
+}
+
+/*!
+  \brief Transfer a call to dest, if the channel supports transfer
+
+  \param chan channel to transfer
+  \param dest destination to transfer to
+  \param protocol is the protocol result
+  SIP example, 0=success, 3xx-6xx is SIP error code
+
+  Called by:
+	\arg app_transfer
+	\arg the manager interface
+*/
+int ast_transfer_protocol(struct ast_channel *chan, char *dest, int *protocol)
 {
 	int res = -1;
 
+	if (protocol) {
+		*protocol = 0;
+	}
+
 	/* Stop if we're a zombie or need a soft hangup */
 	ast_channel_lock(chan);
 	if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
@@ -6510,6 +6532,13 @@ int ast_transfer(struct ast_channel *chan, char *dest)
 				res = 1;
 			} else {
 				res = -1;
+				/* Message can contain a protocol specific code
+				   AST_TRANSFER_SUCCESS indicates success
+				   Else, failure.  Protocol will be set to the failure reason.
+				   SIP example, 0 is success, else error code 3xx-6xx */
+				if (protocol) {
+					*protocol = *message;
+				}
 			}
 
 			ast_frfree(fr);