diff --git a/apps/app_dial.c b/apps/app_dial.c
index c9bee19b0ba4bd6cd40e587dda5e7ddd44261d95..fdbe05692d3303d4f0df87f4a44b162143847b54 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -1007,7 +1007,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
 			ast_channel_unlock(c);
 
 			ast_channel_lock_both(original, in);
-			ast_channel_publish_dial_forward(in, original, NULL, "CANCEL",
+			ast_channel_publish_dial_forward(in, original, c, NULL, "CANCEL",
 				ast_channel_call_forward(c));
 			ast_channel_unlock(in);
 			ast_channel_unlock(original);
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 906dff15fc65ad600966cfcc96d2836ee2bc0138..d9f0f85d3f500ca7afa50957fe5922f11c14c661 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -4666,7 +4666,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
 					ast_channel_unlock(qe->chan);
 
 					ast_channel_lock_both(qe->chan, original);
-					ast_channel_publish_dial_forward(qe->chan, original, NULL, "CANCEL",
+					ast_channel_publish_dial_forward(qe->chan, original, o->chan, NULL, "CANCEL",
 						ast_channel_call_forward(original));
 					ast_channel_unlock(original);
 					ast_channel_unlock(qe->chan);
diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h
index 519a4b676e17c05715f1d0d1a7abcfead47c6fbe..8c27803dfd8a18c6b5b95ec965039cc13fcd69aa 100644
--- a/include/asterisk/stasis_channels.h
+++ b/include/asterisk/stasis_channels.h
@@ -518,12 +518,14 @@ void ast_channel_publish_dial(struct ast_channel *caller,
  *
  * \param caller The channel performing the dial operation
  * \param peer The channel being dialed
+ * \param forwarded The channel created as a result of the call forwarding
  * \param dialstring The information passed to the dialing application when beginning a dial
  * \param dialstatus The current status of the dial operation
  * \param forward The call forward string provided by the dialed channel
  */
 void ast_channel_publish_dial_forward(struct ast_channel *caller,
 		struct ast_channel *peer,
+		struct ast_channel *forwarded,
 		const char *dialstring,
 		const char *dialstatus,
 		const char *forward);
diff --git a/main/dial.c b/main/dial.c
index 134386735af3c1b3a5b69133e7f6c28726028e51..ca0b9c8d1724b45818c0d912db648c5224fc7cbf 100644
--- a/main/dial.c
+++ b/main/dial.c
@@ -465,14 +465,17 @@ static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *c
 	channel->device = ast_strdup(device);
 	AST_LIST_UNLOCK(&dial->channels);
 
-
 	/* Drop the original channel */
-	ast_hangup(original);
 	channel->owner = NULL;
 
 	/* Finally give it a go... send it out into the world */
 	begin_dial_channel(channel, chan, chan ? 0 : 1, predial_string);
 
+	ast_channel_publish_dial_forward(chan, original, channel->owner, NULL, "CANCEL",
+		ast_channel_call_forward(original));
+
+	ast_hangup(original);
+
 	return 0;
 }
 
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index 38aac982e328d75a6fefd0127a7ba76583deb9c0..8a39bdfcb27e0afcd9e851860a06f3c33459fe4f 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -287,14 +287,21 @@ static void channel_blob_dtor(void *obj)
 	ast_json_unref(event->blob);
 }
 
+/*! \brief Dummy callback for receiving events */
+static void dummy_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
+{
+}
+
 void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_channel *peer,
-	const char *dialstring, const char *dialstatus, const char *forward)
+	struct ast_channel *forwarded, const char *dialstring, const char *dialstatus,
+	const char *forward)
 {
 	RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
 	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
 	RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_channel_snapshot *, peer_snapshot, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel_snapshot *, forwarded_snapshot, NULL, ao2_cleanup);
 
 	ast_assert(peer != NULL);
 	blob = ast_json_pack("{s: s, s: s, s: s}",
@@ -323,18 +330,33 @@ void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_cha
 	}
 	ast_multi_channel_blob_add_channel(payload, "peer", peer_snapshot);
 
+	if (forwarded) {
+		forwarded_snapshot = ast_channel_snapshot_create(forwarded);
+		if (!forwarded_snapshot) {
+			return;
+		}
+		ast_multi_channel_blob_add_channel(payload, "forwarded", forwarded_snapshot);
+	}
+
 	msg = stasis_message_create(ast_channel_dial_type(), payload);
 	if (!msg) {
 		return;
 	}
 
-	publish_message_for_channel_topics(msg, caller);
+	if (forwarded) {
+		struct stasis_subscription *subscription = stasis_subscribe(ast_channel_topic(peer), dummy_event_cb, NULL);
+
+		stasis_publish(ast_channel_topic(peer), msg);
+		stasis_unsubscribe_and_join(subscription);
+	} else {
+		publish_message_for_channel_topics(msg, caller);
+	}
 }
 
 void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *peer,
 	const char *dialstring, const char *dialstatus)
 {
-	ast_channel_publish_dial_forward(caller, peer, dialstring, dialstatus, NULL);
+	ast_channel_publish_dial_forward(caller, peer, NULL, dialstring, dialstatus, NULL);
 }
 
 static struct stasis_message *create_channel_blob_message(struct ast_channel_snapshot *snapshot,
@@ -931,11 +953,54 @@ static struct ast_json *hangup_request_to_json(
 	return channel_blob_to_json(message, "ChannelHangupRequest", sanitize);
 }
 
+static struct ast_json *dial_to_json(
+	struct stasis_message *message,
+	const struct stasis_message_sanitizer *sanitize)
+{
+	struct ast_multi_channel_blob *payload = stasis_message_data(message);
+	struct ast_json *blob = ast_multi_channel_blob_get_json(payload);
+	struct ast_json *caller_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "caller"), sanitize);
+	struct ast_json *peer_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "peer"), sanitize);
+	struct ast_json *forwarded_json = ast_channel_snapshot_to_json(ast_multi_channel_blob_get_channel(payload, "forwarded"), sanitize);
+	struct ast_json *json;
+	const struct timeval *tv = stasis_message_timestamp(message);
+	int res = 0;
+
+	json = ast_json_pack("{s: s, s: o, s: O, s: O, s: O}",
+		"type", "Dial",
+		"timestamp", ast_json_timeval(*tv, NULL),
+		"dialstatus", ast_json_object_get(blob, "dialstatus"),
+		"forward", ast_json_object_get(blob, "forward"),
+		"dialstring", ast_json_object_get(blob, "dialstring"));
+	if (!json) {
+		return NULL;
+	}
+
+	if (caller_json) {
+		res |= ast_json_object_set(json, "caller", caller_json);
+	}
+	if (peer_json) {
+		res |= ast_json_object_set(json, "peer", peer_json);
+	}
+	if (forwarded_json) {
+		res |= ast_json_object_set(json, "forwarded", forwarded_json);
+	}
+
+	if (res) {
+		ast_json_unref(json);
+		return NULL;
+	}
+
+	return json;
+}
+
 /*!
  * @{ \brief Define channel message types.
  */
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_snapshot_type);
-STASIS_MESSAGE_TYPE_DEFN(ast_channel_dial_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_dial_type,
+	.to_json = dial_to_json,
+	);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_varset_type,
 	.to_ami = varset_to_ami,
 	.to_json = varset_to_json,
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index 7ddef278dfd877f438807e3b2c751b4b26771cf2..d99240bd8681e8cdab281e98089766721a159e2e 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -2879,6 +2879,137 @@ ari_validator ast_ari_validate_device_state_changed_fn(void)
 	return ast_ari_validate_device_state_changed;
 }
 
+int ast_ari_validate_dial(struct ast_json *json)
+{
+	int res = 1;
+	struct ast_json_iter *iter;
+	int has_type = 0;
+	int has_application = 0;
+	int has_dialstatus = 0;
+	int has_peer = 0;
+
+	for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+		if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_type = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field type failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_application = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field application failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_date(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field timestamp failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("caller", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_channel(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field caller failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("dialstatus", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_dialstatus = 1;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field dialstatus failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("dialstring", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field dialstring failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("forward", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_string(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field forward failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("forwarded", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			prop_is_valid = ast_ari_validate_channel(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field forwarded failed validation\n");
+				res = 0;
+			}
+		} else
+		if (strcmp("peer", ast_json_object_iter_key(iter)) == 0) {
+			int prop_is_valid;
+			has_peer = 1;
+			prop_is_valid = ast_ari_validate_channel(
+				ast_json_object_iter_value(iter));
+			if (!prop_is_valid) {
+				ast_log(LOG_ERROR, "ARI Dial field peer failed validation\n");
+				res = 0;
+			}
+		} else
+		{
+			ast_log(LOG_ERROR,
+				"ARI Dial has undocumented field %s\n",
+				ast_json_object_iter_key(iter));
+			res = 0;
+		}
+	}
+
+	if (!has_type) {
+		ast_log(LOG_ERROR, "ARI Dial missing required field type\n");
+		res = 0;
+	}
+
+	if (!has_application) {
+		ast_log(LOG_ERROR, "ARI Dial missing required field application\n");
+		res = 0;
+	}
+
+	if (!has_dialstatus) {
+		ast_log(LOG_ERROR, "ARI Dial missing required field dialstatus\n");
+		res = 0;
+	}
+
+	if (!has_peer) {
+		ast_log(LOG_ERROR, "ARI Dial missing required field peer\n");
+		res = 0;
+	}
+
+	return res;
+}
+
+ari_validator ast_ari_validate_dial_fn(void)
+{
+	return ast_ari_validate_dial;
+}
+
 int ast_ari_validate_endpoint_state_change(struct ast_json *json)
 {
 	int res = 1;
@@ -3023,6 +3154,9 @@ int ast_ari_validate_event(struct ast_json *json)
 	if (strcmp("DeviceStateChanged", discriminator) == 0) {
 		return ast_ari_validate_device_state_changed(json);
 	} else
+	if (strcmp("Dial", discriminator) == 0) {
+		return ast_ari_validate_dial(json);
+	} else
 	if (strcmp("EndpointStateChange", discriminator) == 0) {
 		return ast_ari_validate_endpoint_state_change(json);
 	} else
@@ -3173,6 +3307,9 @@ int ast_ari_validate_message(struct ast_json *json)
 	if (strcmp("DeviceStateChanged", discriminator) == 0) {
 		return ast_ari_validate_device_state_changed(json);
 	} else
+	if (strcmp("Dial", discriminator) == 0) {
+		return ast_ari_validate_dial(json);
+	} else
 	if (strcmp("EndpointStateChange", discriminator) == 0) {
 		return ast_ari_validate_endpoint_state_change(json);
 	} else
diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h
index 1f9420cb0a9ee75c8205eedef1fd758e3a743891..22ab43be8990274ebde7895b581db03eeb14a1f4 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -790,6 +790,24 @@ int ast_ari_validate_device_state_changed(struct ast_json *json);
  */
 ari_validator ast_ari_validate_device_state_changed_fn(void);
 
+/*!
+ * \brief Validator for Dial.
+ *
+ * Dialing state has changed.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_dial(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_dial().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_dial_fn(void);
+
 /*!
  * \brief Validator for EndpointStateChange.
  *
@@ -1187,6 +1205,16 @@ ari_validator ast_ari_validate_application_fn(void);
  * - application: string (required)
  * - timestamp: Date
  * - device_state: DeviceState (required)
+ * Dial
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - caller: Channel
+ * - dialstatus: string (required)
+ * - dialstring: string
+ * - forward: string
+ * - forwarded: Channel
+ * - peer: Channel (required)
  * EndpointStateChange
  * - type: string (required)
  * - application: string (required)
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 8ad41e565bae337b109a375b305c7bd201b1c4b0..8e9872aec35ae9e03dcf931c09caa50931234f69 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -265,6 +265,25 @@ static void app_dtor(void *obj)
 	app->data = NULL;
 }
 
+static void call_forwarded_handler(struct stasis_app *app, struct stasis_message *message)
+{
+	struct ast_multi_channel_blob *payload = stasis_message_data(message);
+	struct ast_channel_snapshot *snapshot = ast_multi_channel_blob_get_channel(payload, "forwarded");
+	struct ast_channel *chan;
+
+	if (!snapshot) {
+		return;
+	}
+
+	chan = ast_channel_get_by_name(snapshot->uniqueid);
+	if (!chan) {
+		return;
+	}
+
+	app_subscribe_channel(app, chan);
+	ast_channel_unref(chan);
+}
+
 static void sub_default_handler(void *data, struct stasis_subscription *sub,
 	struct stasis_message *message)
 {
@@ -275,6 +294,10 @@ static void sub_default_handler(void *data, struct stasis_subscription *sub,
 		ao2_cleanup(app);
 	}
 
+	if (stasis_message_type(message) == ast_channel_dial_type()) {
+		call_forwarded_handler(app, message);
+	}
+
 	/* By default, send any message that has a JSON representation */
 	json = stasis_message_to_json(message, stasis_app_get_sanitizer());
 	if (!json) {
diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json
index 5195a5bbbde53677e467fa0fa5544b0d8f3e0454..a0c5408fc91327776aec1691b11d82fdd5eb2b8f 100644
--- a/rest-api/api-docs/events.json
+++ b/rest-api/api-docs/events.json
@@ -98,6 +98,7 @@
 				"ChannelHangupRequest",
 				"ChannelVarset",
 				"EndpointStateChange",
+				"Dial",
 				"StasisEnd",
 				"StasisStart"
 			]
@@ -411,6 +412,42 @@
 				}
 			}
 		},
+		"Dial": {
+			"id": "Dial",
+			"description": "Dialing state has changed.",
+			"properties": {
+				"caller": {
+					"required": false,
+					"type": "Channel",
+					"description": "The calling channel."
+				},
+				"peer": {
+					"required": true,
+					"type": "Channel",
+					"description": "The dialed channel."
+				},
+				"forward": {
+					"required": false,
+					"type": "string",
+					"description": "Forwarding target requested by the original dialed channel."
+				},
+				"forwarded": {
+					"required": false,
+					"type": "Channel",
+					"description": "Channel that the caller has been forwarded to."
+				},
+				"dialstring": {
+					"required": false,
+					"type": "string",
+					"description": "The dial string for calling the peer channel."
+				},
+				"dialstatus": {
+					"required": true,
+					"type": "string",
+					"description": "Current status of the dialing attempt to the peer."
+				}
+			}
+		},
 		"StasisEnd": {
 			"id": "StasisEnd",
 			"description": "Notification that a channel has left a Stasis application.",