diff --git a/include/asterisk/res_pjsip_pubsub.h b/include/asterisk/res_pjsip_pubsub.h
index d46cbae30d38896c5e5d73dda7ee7c16296a2279..bd55a448f9650b99bd1b43cd1a1ae9ef37707362 100644
--- a/include/asterisk/res_pjsip_pubsub.h
+++ b/include/asterisk/res_pjsip_pubsub.h
@@ -34,6 +34,15 @@ struct ast_datastore_info;
  */
 struct ast_sip_publication;
 
+enum ast_sip_publish_state {
+    /*! Publication has just been initialized */
+    AST_SIP_PUBLISH_STATE_INITIALIZED,
+    /*! Publication is currently active */
+    AST_SIP_PUBLISH_STATE_ACTIVE,
+    /*! Publication has been terminated */
+    AST_SIP_PUBLISH_STATE_TERMINATED,
+};
+
 /*!
  * \brief Callbacks that publication handlers will define
  */
@@ -47,61 +56,39 @@ struct ast_sip_publish_handler {
 	/*!
 	 * \brief Called when a PUBLISH to establish a new publication arrives.
 	 *
-	 * \param endpoint The endpoint from whom the PUBLISH arrived
-	 * \param rdata The PUBLISH request
-	 * \retval NULL PUBLISH was not accepted
-	 * \retval non-NULL New publication
-	 *
-	 * \note The callback is expected to send a response for the PUBLISH in success cases.
+	 * \param endpoint The endpoint from whom the PUBLISH arrived.
+	 * \param resource The resource whose state is being published.
+	 * \return Response code for the incoming PUBLISH
 	 */
-	struct ast_sip_publication *(*new_publication)(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
-
-	/*!
-	 * \brief Called when a PUBLISH for an existing publication arrives.
-	 *
-	 * This PUBLISH may be intending to change state or it may be simply renewing
-	 * the publication since the publication is nearing expiration. The callback
-	 * is expected to send a response to the PUBLISH.
-	 *
-	 * \param pub The publication on which the PUBLISH arrived
-	 * \param rdata The PUBLISH request
-	 * \retval 0 Publication was accepted
-	 * \retval non-zero Publication was denied
-	 *
-	 * \note The callback is expected to send a response for the PUBLISH.
-	 */
-	int (*publish_refresh)(struct ast_sip_publication *pub, pjsip_rx_data *rdata);
-
+	int (*new_publication)(struct ast_sip_endpoint *endpoint, const char *resource);
 	/*!
 	 * \brief Called when a publication has reached its expiration.
 	 */
 	void (*publish_expire)(struct ast_sip_publication *pub);
-
 	/*!
-	 * \brief Called when a PUBLISH arrives to terminate a publication.
+	 * \brief Published resource has changed states.
+	 *
+	 * The state parameter can be used to take further action. For instance,
+	 * if the state is AST_SIP_PUBLISH_STATE_INITIALIZED, then this is the initial
+	 * PUBLISH request. This is a good time to set up datastores on the publication
+	 * or any other initial needs.
+	 *
+	 * AST_SIP_PUBLISH_STATE_TERMINATED is used when the remote end is terminating
+	 * its publication. This is a good opportunity to free any resources associated with
+	 * the publication.
 	 *
-	 * \param pub The publication that is terminating
-	 * \param rdata The PUBLISH request terminating the publication
+	 * AST_SIP_PUBLISH_STATE_ACTIVE is used when a publication that modifies state
+	 * arrives.
 	 *
-	 * \note The callback is expected to send a response for the PUBLISH.
+	 * \param pub The publication whose state has changed
+	 * \param body The body of the inbound PUBLISH
+	 * \param state The state of the publication
 	 */
-	void (*publish_termination)(struct ast_sip_publication *pub, pjsip_rx_data *rdata);
-
+	int (*publication_state_change)(struct ast_sip_publication *pub, pjsip_msg_body *body,
+			enum ast_sip_publish_state state);
 	AST_LIST_ENTRY(ast_sip_publish_handler) next;
 };
 
-/*!
- * \brief Create a new publication
- *
- * Publication handlers should call this when a PUBLISH arrives to establish a new publication.
- *
- * \param endpoint The endpoint from whom the PUBLISHes arrive
- * \param rdata The PUBLISH that established the publication
- * \retval NULL Failed to create a publication
- * \retval non-NULL The newly-created publication
- */
-struct ast_sip_publication *ast_sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
-
 /*!
  * \brief Given a publication, get the associated endpoint
  *
@@ -111,29 +98,6 @@ struct ast_sip_publication *ast_sip_create_publication(struct ast_sip_endpoint *
  */
 struct ast_sip_endpoint *ast_sip_publication_get_endpoint(struct ast_sip_publication *pub);
 
-/*!
- * \brief Create a response to an inbound PUBLISH
- *
- * The created response must be sent using ast_sip_publication_send_response
- *
- * \param pub The publication
- * \param status code The status code to place in the response
- * \param rdata The request to which the response is being made
- * \param[out] tdata The created response
- */
-int ast_sip_publication_create_response(struct ast_sip_publication *pub, int status_code, pjsip_rx_data *rdata,
-	pjsip_tx_data **tdata);
-
-/*!
- * \brief Send a response for an inbound PUBLISH
- *
- * \param pub The publication
- * \param rdata The request to which the response was made
- * \param tdata The response to the request
- */
-pj_status_t ast_sip_publication_send_response(struct ast_sip_publication *pub, pjsip_rx_data *rdata,
-	pjsip_tx_data *tdata);
-
 /*!
  * \brief Register a publish handler
  *
@@ -222,19 +186,20 @@ struct ast_sip_subscription_response_data {
 };
 
 #define AST_SIP_MAX_ACCEPT 32
+enum ast_sip_subscription_notify_reason {
+	/*! Initial NOTIFY for subscription */
+	AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED,
+	/*! Subscription has been renewed */
+	AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED,
+	/*! Subscription is being terminated */
+	AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED,
+	/*! Other unspecified reason */
+	AST_SIP_SUBSCRIPTION_NOTIFY_REASON_OTHER
+};
 
-struct ast_sip_subscription_handler {
-	/*! The name of the event this handler deals with */
-	const char *event_name;
-	/*! The types of body this handler accepts.
-	 *
-	 * \note This option has no bearing when the handler is used in the
-	 * notifier role. When in a subscriber role, this header is used to
-	 * populate the Accept: header of an outbound SUBSCRIBE request
-	 */
-	const char *accept[AST_SIP_MAX_ACCEPT];
+struct ast_sip_notifier {
 	/*!
-	 * \brief Default body type defined for the event package this handler handles.
+	 * \brief Default body type defined for the event package this notifier handles.
 	 *
 	 * Typically, a SUBSCRIBE request will contain one or more Accept headers that tell
 	 * what format they expect the body of NOTIFY requests to use. However, every event
@@ -243,163 +208,96 @@ struct ast_sip_subscription_handler {
 	 */
 	const char *default_accept;
 	/*!
-	 * \brief Called when a subscription is to be destroyed
+	 * \brief Called when a SUBSCRIBE arrives attempting to establish a new subscription.
 	 *
-	 * This is a subscriber and notifier callback.
+	 * The notifier is expected to return the response that should be sent to the
+	 * SUBSCRIBE request.
 	 *
-	 * The handler is not expected to send any sort of requests or responses
-	 * during this callback. The handler MUST, however, begin the destruction
-	 * process for the subscription during this callback.
-	 */
-	void (*subscription_shutdown)(struct ast_sip_subscription *subscription);
-
-	/*!
-	 * \brief Called when a SUBSCRIBE arrives in order to create a new subscription
-	 *
-	 * This is a notifier callback.
-	 *
-	 * If the notifier wishes to accept the subscription, then it can create
-	 * a new ast_sip_subscription to do so.
-	 *
-	 * If the notifier chooses to create a new subscription, then it must accept
-	 * the incoming subscription using pjsip_evsub_accept() and it must also
-	 * send an initial NOTIFY with the current subscription state.
+	 * If a 200-class response is returned, then the notifier's notify_required
+	 * callback will immediately be called into with a reason of
+	 * AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED.
 	 *
 	 * \param endpoint The endpoint from which we received the SUBSCRIBE
-	 * \param rdata The SUBSCRIBE request
-	 * \retval NULL The SUBSCRIBE has not been accepted
-	 * \retval non-NULL The newly-created subscription
-	 */
-	struct ast_sip_subscription *(*new_subscribe)(struct ast_sip_endpoint *endpoint,
-			pjsip_rx_data *rdata);
-
-	/*!
-	 * \brief Called when an endpoint renews a subscription.
-	 *
-	 * This is a notifier callback.
-	 *
-	 * Because of the way that the PJSIP evsub framework works, it will automatically
-	 * send a response to the SUBSCRIBE. However, the subscription handler must send
-	 * a NOTIFY with the current subscription state when this callback is called.
-	 *
-	 * The response_data that is passed into this callback is used to craft what should
-	 * be in the response to the incoming SUBSCRIBE. It is initialized with a 200 status
-	 * code and all other parameters are empty.
-	 *
-	 * \param sub The subscription that is being renewed
-	 * \param rdata The SUBSCRIBE request in question
-	 * \param[out] response_data Data pertaining to the SIP response that should be
-	 * sent to the SUBSCRIBE
+	 * \param resource The name of the resource to which the subscription is being made
+	 * \return The response code to send to the SUBSCRIBE.
 	 */
-	void (*resubscribe)(struct ast_sip_subscription *sub,
-			pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data);
-
+	int (*new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource);
 	/*!
-	 * \brief Called when a subscription times out.
+	 * \brief The subscription is in need of a NOTIFY request.
 	 *
-	 * This is a notifier callback
+	 * A reason of AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED is given immediately
+	 * after a SUBSCRIBE is accepted. This is a good opportunity for the notifier to
+	 * perform setup duties such as establishing Stasis subscriptions or adding
+	 * datastores to the subscription.
 	 *
-	 * This indicates that the subscription has timed out. The subscription handler is
-	 * expected to send a NOTIFY that terminates the subscription.
+	 * A reason of AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED is given when the
+	 * subscriber has terminated the subscription. If there are any duties that the
 	 *
-	 * \param sub The subscription that has timed out
-	 */
-	void (*subscription_timeout)(struct ast_sip_subscription *sub);
-
-	/*!
-	 * \brief Called when a subscription is terminated via a SUBSCRIBE or NOTIFY request
-	 *
-	 * This is a notifier and subscriber callback.
-	 *
-	 * The PJSIP subscription framework will automatically send the response to the
-	 * request. If a notifier receives this callback, then the subscription handler
-	 * is expected to send a final NOTIFY to terminate the subscription.
 	 *
-	 * \param sub The subscription being terminated
-	 * \param rdata The request that terminated the subscription
-	 */
-	void (*subscription_terminated)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata);
-
-	/*!
-	 * \brief Called when a subscription handler's outbound NOTIFY receives a response
-	 *
-	 * This is a notifier callback.
-	 *
-	 * \param sub The subscription
-	 * \param rdata The NOTIFY response
+	 * \param sub The subscription to send the NOTIFY on.
+	 * \param reason The reason why the NOTIFY is being sent.
+	 * \retval 0 Success
+	 * \retval -1 Failure
 	 */
-	void (*notify_response)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata);
+	int (*notify_required)(struct ast_sip_subscription *sub, enum ast_sip_subscription_notify_reason reason);
+};
 
+struct ast_sip_subscriber {
 	/*!
-	 * \brief Called when a subscription handler receives an inbound NOTIFY
-	 *
-	 * This is a subscriber callback.
+	 * \brief A NOTIFY has been received.
 	 *
-	 * Because of the way that the PJSIP evsub framework works, it will automatically
-	 * send a response to the NOTIFY. By default this will be a 200 OK response, but
-	 * this callback can change details of the response by returning response data
-	 * to use.
+	 * The body of the NOTIFY is provided so that it may be parsed and appropriate
+	 * internal state change may be generated.
 	 *
-	 * The response_data that is passed into this callback is used to craft what should
-	 * be in the response to the incoming SUBSCRIBE. It is initialized with a 200 status
-	 * code and all other parameters are empty.
+	 * The state can be used to determine if the subscription has been terminated
+	 * by the far end or if this is just a typical resource state change.
 	 *
-	 * \param sub The subscription
-	 * \param rdata The NOTIFY request
-	 * \param[out] response_data Data pertaining to the SIP response that should be
-	 * sent to the SUBSCRIBE
+	 * \param sub The subscription on which the NOTIFY arrived
+	 * \param body The body of the NOTIFY
+	 * \param state The subscription state
 	 */
-	void (*notify_request)(struct ast_sip_subscription *sub,
-			pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data);
+	void (*state_change)(struct ast_sip_subscription *sub, pjsip_msg_body *body, enum pjsip_evsub_state state);
+};
 
+struct ast_sip_subscription_handler {
+	/*! The name of the event this subscriber deals with */
+	const char *event_name;
+	/*! The types of body this subscriber accepts. */
+	const char *accept[AST_SIP_MAX_ACCEPT];
 	/*!
-	 * \brief Called when it is time for a subscriber to resubscribe
-	 *
-	 * This is a subscriber callback.
-	 *
-	 * The subscriber can reresh the subscription using the pjsip_evsub_initiate()
-	 * function.
+	 * \brief Called when a subscription is to be destroyed
 	 *
-	 * \param sub The subscription to refresh
-	 * \retval 0 Success
-	 * \retval non-zero Failure
+	 * The handler is not expected to send any sort of requests or responses
+	 * during this callback. The handler MUST, however, begin the destruction
+	 * process for the subscription during this callback.
 	 */
-	int (*refresh_subscription)(struct ast_sip_subscription *sub);
-
+	void (*subscription_shutdown)(struct ast_sip_subscription *subscription);
 	/*!
 	 * \brief Converts the subscriber to AMI
 	 *
-	 * This is a subscriber callback.
-	 *
 	 * \param sub The subscription
 	 * \param buf The string to write AMI data
 	 */
 	void (*to_ami)(struct ast_sip_subscription *sub, struct ast_str **buf);
+	/*! Subscriber callbacks for this handler */
+	struct ast_sip_subscriber *subscriber;
+	/*! Notifier callbacks for this handler */
+	struct ast_sip_notifier *notifier;
 	AST_LIST_ENTRY(ast_sip_subscription_handler) next;
 };
 
 /*!
  * \brief Create a new ast_sip_subscription structure
  *
- * In most cases the pubsub core will create a general purpose subscription
- * within PJSIP. However, PJSIP provides enhanced support for the following
- * event packages:
- *
- * presence
- * message-summary
+ * When a subscriber wishes to create a subscription, it may call this function
+ * to allocate resources and to send the initial SUBSCRIBE out.
  *
- * If either of these events are handled by the subscription handler, then
- * the special-purpose event subscriptions will be created within PJSIP,
- * and it will be expected that your subscription handler make use of the
- * special PJSIP APIs.
- *
- * \param handler The subsription handler for this subscription
- * \param role Whether we are acting as subscriber or notifier for this subscription
- * \param endpoint The endpoint involved in this subscription
- * \param rdata If acting as a notifier, the SUBSCRIBE request that triggered subscription creation
+ * \param subscriber The subscriber that is making the request.
+ * \param endpoint The endpoint to whome the SUBSCRIBE will be sent.
+ * \param resource The resource to place in the SUBSCRIBE's Request-URI.
  */
 struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
-		enum ast_sip_subscription_role role, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata);
+		struct ast_sip_endpoint *endpoint, const char *resource);
 
 
 /*!
@@ -426,46 +324,61 @@ struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscr
 struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub);
 
 /*!
- * \brief Get the underlying PJSIP evsub structure
+ * \brief Notify a SIP subscription of a state change.
  *
- * This is useful when wishing to call PJSIP's API calls in order to
- * create SUBSCRIBEs, NOTIFIES, etc. as well as get subscription state
+ * This will create a NOTIFY body to be sent out for the subscribed resource.
+ * On real subscriptions, a NOTIFY request will be generated and sent.
+ * On virtual subscriptions, the NOTIFY is saved on the virtual subscription and the
+ * parent subscription is alerted.
  *
- * This function, as well as all methods called on the pjsip_evsub should
- * be done in a SIP servant thread.
+ * \param sub The subscription on which a state change is occurring.
+ * \param notify_data Event package-specific data used to create the NOTIFY body.
+ * \param terminate True if this NOTIFY is intended to terminate the subscription.
+ * \retval 0 Success
+ * \retval non-zero Failure
+ */
+int ast_sip_subscription_notify(struct ast_sip_subscription *sub, void *notify_data, int terminate);
+
+/*!
+ * \brief Retrieve the local URI for this subscription
+ *
+ * This is the local URI as determined by the underlying SIP dialog.
  *
  * \param sub The subscription
- * \retval NULL Failure
- * \retval non-NULL The underlying pjsip_evsub
+ * \param[out] buf The buffer into which to store the URI.
+ * \param size The size of the buffer.
  */
-pjsip_evsub *ast_sip_subscription_get_evsub(struct ast_sip_subscription *sub);
+void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size);
 
 /*!
- * \brief Get the underlying PJSIP dialog structure
- *
- * Call this function when information needs to be retrieved from the
- * underlying pjsip dialog.
+ * \brief Retrive the remote URI for this subscription
  *
- * This function, as well as all methods called on the pjsip_evsub should
- * be done in a SIP servant thread.
+ * This is the remote URI as determined by the underlying SIP dialog.
  *
  * \param sub The subscription
- * \retval NULL Failure
- * \retval non-NULL The underlying pjsip_dialog
+ * \param[out] buf The buffer into which to store the URI.
+ * \param size The size of the buffer.
+ */
+void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size);
+
+/*!
+ * \brief Get the name of the subscribed resource.
  */
-pjsip_dialog *ast_sip_subscription_get_dlg(struct ast_sip_subscription *sub);
+const char *ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub);
 
 /*!
- * \brief Accept a subscription request
+ * \brief Get a header value for a subscription.
  *
- * \param sub The subscription to be accepted
- * \param rdata The received subscription request
- * \param response The response code to send
+ * For notifiers, the headers of the inbound SUBSCRIBE that started the dialog
+ * are stored on the subscription. This method allows access to the header. The
+ * return is the same as pjsip_msg_find_hdr_by_name(), meaning that it is dependent
+ * on the header being searched for.
  *
- * \retval 0 Success
- * \retval non-zero Failure
+ * \param sub The subscription to search in.
+ * \param header The name of the header to search for.
+ * \return The discovered header, or NULL if the header cannot be found.
  */
-int ast_sip_subscription_accept(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, int response);
+void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header);
 
 /*!
  * \brief Send a request created via a PJSIP evsub method
diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c
index f4bfef772e093eeeee85aaffa3bb73d512dd548c..cb39026ca2e906135962cc5bfa047d0d030c18ec 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -68,26 +68,24 @@ struct exten_state_subscription {
 #define DEFAULT_PRESENCE_BODY "application/pidf+xml"
 
 static void subscription_shutdown(struct ast_sip_subscription *sub);
-static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpoint,
-						  pjsip_rx_data *rdata);
-static void resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
-			struct ast_sip_subscription_response_data *response_data);
-static void subscription_timeout(struct ast_sip_subscription *sub);
-static void subscription_terminated(struct ast_sip_subscription *sub,
-				    pjsip_rx_data *rdata);
+static int new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource);
+static int notify_required(struct ast_sip_subscription *sub,
+		enum ast_sip_subscription_notify_reason reason);
 static void to_ami(struct ast_sip_subscription *sub,
 		   struct ast_str **buf);
 
+struct ast_sip_notifier presence_notifier = {
+	.default_accept = DEFAULT_PRESENCE_BODY,
+	.new_subscribe = new_subscribe,
+	.notify_required = notify_required,
+};
+
 struct ast_sip_subscription_handler presence_handler = {
 	.event_name = "presence",
 	.accept = { DEFAULT_PRESENCE_BODY, },
-	.default_accept = DEFAULT_PRESENCE_BODY,
 	.subscription_shutdown = subscription_shutdown,
-	.new_subscribe = new_subscribe,
-	.resubscribe = resubscribe,
-	.subscription_timeout = subscription_timeout,
-	.subscription_terminated = subscription_terminated,
 	.to_ami = to_ami,
+	.notifier = &presence_notifier,
 };
 
 static void exten_state_subscription_destructor(void *obj)
@@ -98,14 +96,12 @@ static void exten_state_subscription_destructor(void *obj)
 	ao2_cleanup(sub->sip_sub);
 }
 
-static char *get_user_agent(pjsip_rx_data *rdata)
+static char *get_user_agent(const struct ast_sip_subscription *sip_sub)
 {
-	static const pj_str_t USER_AGENT = { "User-Agent", 10 };
-
 	size_t size;
 	char *user_agent = NULL;
-	pjsip_user_agent_hdr *user_agent_hdr = pjsip_msg_find_hdr_by_name(
-		rdata->msg_info.msg, &USER_AGENT, NULL);
+	pjsip_user_agent_hdr *user_agent_hdr = ast_sip_subscription_get_header(
+			sip_sub, "User-Agent");
 
 	if (!user_agent_hdr) {
 		return NULL;
@@ -132,85 +128,30 @@ static char *get_user_agent(pjsip_rx_data *rdata)
  * sure that there are registered handler and provider objects available.
  */
 static struct exten_state_subscription *exten_state_subscription_alloc(
-	struct ast_sip_endpoint *endpoint, enum ast_sip_subscription_role role, pjsip_rx_data *rdata)
+		struct ast_sip_subscription *sip_sub, struct ast_sip_endpoint *endpoint)
 {
-	RAII_VAR(struct exten_state_subscription *, exten_state_sub,
-		 ao2_alloc(sizeof(*exten_state_sub), exten_state_subscription_destructor), ao2_cleanup);
+	struct exten_state_subscription * exten_state_sub;
 
+	exten_state_sub = ao2_alloc(sizeof(*exten_state_sub), exten_state_subscription_destructor);
 	if (!exten_state_sub) {
 		return NULL;
 	}
 
-	if (!(exten_state_sub->sip_sub = ast_sip_create_subscription(
-		      &presence_handler, role, endpoint, rdata))) {
-		ast_log(LOG_WARNING, "Unable to create SIP subscription for endpoint %s\n",
-			ast_sorcery_object_get_id(endpoint));
-		return NULL;
-	}
-
+	exten_state_sub->sip_sub = ao2_bump(sip_sub);
 	exten_state_sub->last_exten_state = INITIAL_LAST_EXTEN_STATE;
 	exten_state_sub->last_presence_state = AST_PRESENCE_NOT_SET;
-	exten_state_sub->user_agent = get_user_agent(rdata);
-	ao2_ref(exten_state_sub, +1);
+	exten_state_sub->user_agent = get_user_agent(sip_sub);
 	return exten_state_sub;
 }
 
-/*!
- * \internal
- * \brief Create and send a NOTIFY request to the subscriber.
- */
-static void create_send_notify(struct exten_state_subscription *exten_state_sub, const char *reason,
-			       pjsip_evsub_state evsub_state, struct ast_sip_exten_state_data *exten_state_data)
-{
-	RAII_VAR(struct ast_str *, body_text, ast_str_create(BODY_SIZE), ast_free_ptr);
-	pj_str_t reason_str;
-	const pj_str_t *reason_str_ptr = NULL;
-	pjsip_tx_data *tdata;
-	struct ast_sip_body body;
-
-	body.type = ast_sip_subscription_get_body_type(exten_state_sub->sip_sub);
-	body.subtype = ast_sip_subscription_get_body_subtype(exten_state_sub->sip_sub);
-
-	if (ast_sip_pubsub_generate_body_content(body.type, body.subtype,
-				exten_state_data, &body_text)) {
-		ast_log(LOG_ERROR, "Unable to create body on NOTIFY request\n");
-		return;
-	}
-
-	body.body_text = ast_str_buffer(body_text);
-
-	if (reason) {
-		pj_cstr(&reason_str, reason);
-		reason_str_ptr = &reason_str;
-	}
-
-	if (pjsip_evsub_notify(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub),
-			      evsub_state, NULL, reason_str_ptr, &tdata) != PJ_SUCCESS) {
-		ast_log(LOG_WARNING, "Unable to create NOTIFY request\n");
-		return;
-	}
-
-	if (ast_sip_add_body(tdata, &body)) {
-		ast_log(LOG_WARNING, "Unable to add body to NOTIFY request\n");
-		pjsip_tx_data_dec_ref(tdata);
-		return;
-	}
-
-	if (ast_sip_subscription_send_request(exten_state_sub->sip_sub, tdata) != PJ_SUCCESS) {
-		ast_log(LOG_WARNING, "Unable to send NOTIFY request\n");
-	}
-}
-
 /*!
  * \internal
  * \brief Get device state information and send notification to the subscriber.
  */
-static void send_notify(struct exten_state_subscription *exten_state_sub, const char *reason,
-	pjsip_evsub_state evsub_state)
+static void send_notify(struct exten_state_subscription *exten_state_sub)
 {
 	RAII_VAR(struct ao2_container*, info, NULL, ao2_cleanup);
 	char *subtype = NULL, *message = NULL;
-	pjsip_dialog *dlg;
 	struct ast_sip_exten_state_data exten_state_data = {
 		.exten = exten_state_sub->exten,
 		.presence_state = ast_hint_presence_state(NULL, exten_state_sub->context,
@@ -220,11 +161,10 @@ static void send_notify(struct exten_state_subscription *exten_state_sub, const
 		.user_agent = exten_state_sub->user_agent
 	};
 
-	dlg = ast_sip_subscription_get_dlg(exten_state_sub->sip_sub);
-	ast_copy_pj_str(exten_state_data.local, &dlg->local.info_str,
-			sizeof(exten_state_data.local));
-	ast_copy_pj_str(exten_state_data.remote, &dlg->remote.info_str,
-			sizeof(exten_state_data.remote));
+	ast_sip_subscription_get_local_uri(exten_state_sub->sip_sub,
+			exten_state_data.local, sizeof(exten_state_data.local));
+	ast_sip_subscription_get_remote_uri(exten_state_sub->sip_sub,
+			exten_state_data.remote, sizeof(exten_state_data.remote));
 
 	if ((exten_state_data.exten_state = ast_extension_state_extended(
 		     NULL, exten_state_sub->context, exten_state_sub->exten, &info)) < 0) {
@@ -238,14 +178,14 @@ static void send_notify(struct exten_state_subscription *exten_state_sub, const
 			"exten_state", 1024, 1024);
 
 	exten_state_data.device_state_info = info;
-	create_send_notify(exten_state_sub, reason, evsub_state, &exten_state_data);
+	ast_sip_subscription_notify(exten_state_sub->sip_sub, &exten_state_data, 0);
 	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), exten_state_data.pool);
 }
 
 struct notify_task_data {
 	struct ast_sip_exten_state_data exten_state_data;
 	struct exten_state_subscription *exten_state_sub;
-	pjsip_evsub_state evsub_state;
+	int terminate;
 };
 
 static void notify_task_data_destructor(void *obj)
@@ -264,14 +204,12 @@ static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten
 {
 	struct notify_task_data *task_data =
 		ao2_alloc(sizeof(*task_data), notify_task_data_destructor);
-	struct pjsip_dialog *dlg;
 
 	if (!task_data) {
 		ast_log(LOG_WARNING, "Unable to create notify task data\n");
 		return NULL;
 	}
 
-	task_data->evsub_state = PJSIP_EVSUB_STATE_ACTIVE;
 	task_data->exten_state_sub = exten_state_sub;
 	task_data->exten_state_sub->last_exten_state = info->exten_state;
 	task_data->exten_state_sub->last_presence_state = info->presence_state;
@@ -289,17 +227,16 @@ static struct notify_task_data *alloc_notify_task_data(char *exten, struct exten
 		ao2_ref(task_data->exten_state_data.device_state_info, +1);
 	}
 
-	dlg = ast_sip_subscription_get_dlg(exten_state_sub->sip_sub);
-	ast_copy_pj_str(task_data->exten_state_data.local, &dlg->local.info_str,
-			sizeof(task_data->exten_state_data.local));
-	ast_copy_pj_str(task_data->exten_state_data.remote, &dlg->remote.info_str,
-			sizeof(task_data->exten_state_data.remote));
+	ast_sip_subscription_get_local_uri(exten_state_sub->sip_sub,
+			task_data->exten_state_data.local, sizeof(task_data->exten_state_data.local));
+	ast_sip_subscription_get_remote_uri(exten_state_sub->sip_sub,
+			task_data->exten_state_data.remote, sizeof(task_data->exten_state_data.remote));
 
 	if ((info->exten_state == AST_EXTENSION_DEACTIVATED) ||
 	    (info->exten_state == AST_EXTENSION_REMOVED)) {
-		task_data->evsub_state = PJSIP_EVSUB_STATE_TERMINATED;
 		ast_log(LOG_WARNING, "Watcher for hint %s %s\n", exten, info->exten_state
 			 == AST_EXTENSION_REMOVED ? "removed" : "deactivated");
+		task_data->terminate = 1;
 	}
 
 	return task_data;
@@ -313,9 +250,8 @@ static int notify_task(void *obj)
 	task_data->exten_state_data.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
 			"exten_state", 1024, 1024);
 
-	create_send_notify(task_data->exten_state_sub, task_data->evsub_state ==
-			   PJSIP_EVSUB_STATE_TERMINATED ? "noresource" : NULL,
-			   task_data->evsub_state, &task_data->exten_state_data);
+	ast_sip_subscription_notify(task_data->exten_state_sub->sip_sub, &task_data->exten_state_data,
+			task_data->terminate);
 
 	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(),
 			task_data->exten_state_data.pool);
@@ -407,24 +343,30 @@ static void subscription_shutdown(struct ast_sip_subscription *sub)
 	ao2_cleanup(exten_state_sub);
 }
 
-static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpoint,
-						  pjsip_rx_data *rdata)
+static int new_subscribe(struct ast_sip_endpoint *endpoint,
+		const char *resource)
 {
-	pjsip_uri *uri = rdata->msg_info.msg->line.req.uri;
-	pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(uri);
-	RAII_VAR(struct exten_state_subscription *, exten_state_sub, NULL, ao2_cleanup);
-
-	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
-		ast_log(LOG_WARNING, "Attempt to SUBSCRIBE to a non-SIP URI\n");
-		return NULL;
+	if (!ast_exists_extension(NULL, endpoint->context, resource, PRIORITY_HINT, NULL)) {
+		ast_log(LOG_WARNING, "Extension %s does not exist or has no associated hint\n", resource);
+		return 404;
 	}
 
-	if (!(exten_state_sub = exten_state_subscription_alloc(endpoint, AST_SIP_NOTIFIER, rdata))) {
-		return NULL;
+	return 200;
+}
+
+static int initial_subscribe(struct ast_sip_subscription *sip_sub)
+{
+	struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);
+	const char *resource = ast_sip_subscription_get_resource_name(sip_sub);
+	struct exten_state_subscription *exten_state_sub;
+
+	if (!(exten_state_sub = exten_state_subscription_alloc(sip_sub, endpoint))) {
+		ao2_cleanup(endpoint);
+		return -1;
 	}
 
 	ast_copy_string(exten_state_sub->context, endpoint->context, sizeof(exten_state_sub->context));
-	ast_copy_pj_str(exten_state_sub->exten, &sip_uri->user, sizeof(exten_state_sub->exten));
+	ast_copy_string(exten_state_sub->exten, resource, sizeof(exten_state_sub->exten));
 
 	if ((exten_state_sub->id = ast_extension_state_add_destroy_extended(
 		     exten_state_sub->context, exten_state_sub->exten,
@@ -432,64 +374,50 @@ static struct ast_sip_subscription *new_subscribe(struct ast_sip_endpoint *endpo
 		ast_log(LOG_WARNING, "Unable to subscribe endpoint '%s' to extension '%s@%s'\n",
 			ast_sorcery_object_get_id(endpoint), exten_state_sub->exten,
 			exten_state_sub->context);
-		pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE);
-		return NULL;
+		ao2_cleanup(endpoint);
+		ao2_cleanup(exten_state_sub);
+		return -1;
 	}
 
+	/* Go ahead and cleanup the endpoint since we don't need it anymore */
+	ao2_cleanup(endpoint);
+
 	/* bump the ref since ast_extension_state_add holds a reference */
 	ao2_ref(exten_state_sub, +1);
 
 	if (add_datastore(exten_state_sub)) {
 		ast_log(LOG_WARNING, "Unable to add to subscription datastore.\n");
-		pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE);
-		return NULL;
-	}
-
-	if (ast_sip_subscription_accept(exten_state_sub->sip_sub, rdata, 200)) {
-		ast_log(LOG_WARNING, "Unable to accept the incoming extension state subscription.\n");
-		pjsip_evsub_terminate(ast_sip_subscription_get_evsub(exten_state_sub->sip_sub), PJ_FALSE);
-		return NULL;
-	}
-
-	send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_ACTIVE);
-	return exten_state_sub->sip_sub;
-}
-
-static void resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
-			struct ast_sip_subscription_response_data *response_data)
-{
-	struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub);
-
-	if (!exten_state_sub) {
-		return;
+		ao2_cleanup(exten_state_sub);
+		return -1;
 	}
 
-	send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_ACTIVE);
+	send_notify(exten_state_sub);
+	ao2_cleanup(exten_state_sub);
+	return 0;
 }
 
-static void subscription_timeout(struct ast_sip_subscription *sub)
+static int notify_required(struct ast_sip_subscription *sub,
+		enum ast_sip_subscription_notify_reason reason)
 {
-	struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub);
-
-	if (!exten_state_sub) {
-		return;
-	}
+	struct exten_state_subscription *exten_state_sub;
 
-	ast_verbose(VERBOSE_PREFIX_3 "Subscription has timed out.\n");
-	send_notify(exten_state_sub, "timeout", PJSIP_EVSUB_STATE_TERMINATED);
-}
+	switch (reason) {
+	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED:
+		return initial_subscribe(sub);
+	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED:
+	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED:
+	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_OTHER:
+		exten_state_sub = get_exten_state_sub(sub);
 
-static void subscription_terminated(struct ast_sip_subscription *sub,
-				    pjsip_rx_data *rdata)
-{
-	struct exten_state_subscription *exten_state_sub = get_exten_state_sub(sub);
+		if (!exten_state_sub) {
+			return -1;
+		}
 
-	if (!exten_state_sub) {
-		return;
+		send_notify(exten_state_sub);
+		break;
 	}
 
-	ast_verbose(VERBOSE_PREFIX_3 "Subscription has been terminated.\n");
-	send_notify(exten_state_sub, NULL, PJSIP_EVSUB_STATE_TERMINATED);
+	return 0;
 }
 
 static void to_ami(struct ast_sip_subscription *sub,
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index f25a7c48f9714fd7f7221f0d9f578344c0fe5df2..a8e2d14297cfc8a05598f513b12d991c14facbf0 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -49,31 +49,24 @@ AO2_GLOBAL_OBJ_STATIC(unsolicited_mwi);
 #define MWI_SUBTYPE "simple-message-summary"
 
 static void mwi_subscription_shutdown(struct ast_sip_subscription *sub);
-static struct ast_sip_subscription *mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
-		pjsip_rx_data *rdata);
-static void mwi_resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
-		struct ast_sip_subscription_response_data *response_data);
-static void mwi_subscription_timeout(struct ast_sip_subscription *sub);
-static void mwi_subscription_terminated(struct ast_sip_subscription *sub, pjsip_rx_data *rdata);
-static void mwi_notify_response(struct ast_sip_subscription *sub, pjsip_rx_data *rdata);
-static void mwi_notify_request(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
-		struct ast_sip_subscription_response_data *response_data);
-static int mwi_refresh_subscription(struct ast_sip_subscription *sub);
 static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf);
+static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
+		const char *resource);
+static int mwi_notify_required(struct ast_sip_subscription *sip_sub,
+		enum ast_sip_subscription_notify_reason reason);
+
+static struct ast_sip_notifier mwi_notifier = {
+	.default_accept = MWI_TYPE"/"MWI_SUBTYPE,
+	.new_subscribe = mwi_new_subscribe,
+	.notify_required = mwi_notify_required,
+};
 
 static struct ast_sip_subscription_handler mwi_handler = {
 	.event_name = "message-summary",
 	.accept = { MWI_TYPE"/"MWI_SUBTYPE, },
-	.default_accept =  MWI_TYPE"/"MWI_SUBTYPE,
 	.subscription_shutdown = mwi_subscription_shutdown,
-	.new_subscribe = mwi_new_subscribe,
-	.resubscribe = mwi_resubscribe,
-	.subscription_timeout = mwi_subscription_timeout,
-	.subscription_terminated = mwi_subscription_terminated,
-	.notify_response = mwi_notify_response,
-	.notify_request = mwi_notify_request,
-	.refresh_subscription = mwi_refresh_subscription,
 	.to_ami = mwi_to_ami,
+	.notifier = &mwi_notifier,
 };
 
 /*!
@@ -202,7 +195,7 @@ static void mwi_subscription_destructor(void *obj)
 }
 
 static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *endpoint,
-		enum ast_sip_subscription_role role, unsigned int is_solicited, pjsip_rx_data *rdata)
+		unsigned int is_solicited, struct ast_sip_subscription *sip_sub)
 {
 	struct mwi_subscription *sub;
 	const char *endpoint_id = ast_sorcery_object_get_id(endpoint);
@@ -216,6 +209,7 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *
 
 	/* Safe strcpy */
 	strcpy(sub->id, endpoint_id);
+
 	/* Unsolicited MWI doesn't actually result in a SIP subscription being
 	 * created. This is because a SIP subscription associates with a dialog.
 	 * Most devices expect unsolicited MWI NOTIFYs to appear out of dialog. If
@@ -224,13 +218,7 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *
 	 * state not being updated on the device
 	 */
 	if (is_solicited) {
-		sub->sip_sub = ast_sip_create_subscription(&mwi_handler,
-				role, endpoint, rdata);
-		if (!sub->sip_sub) {
-			ast_log(LOG_WARNING, "Unable to create MWI SIP subscription for endpoint %s\n", sub->id);
-			ao2_cleanup(sub);
-			return NULL;
-		}
+		sub->sip_sub = ao2_bump(sip_sub);
 	}
 
 	sub->stasis_subs = ao2_container_alloc(STASIS_BUCKETS, stasis_sub_hash, stasis_sub_cmp);
@@ -314,7 +302,6 @@ struct unsolicited_mwi_data {
 	struct mwi_subscription *sub;
 	struct ast_sip_endpoint *endpoint;
 	pjsip_evsub_state state;
-	const char *reason;
 	const struct ast_sip_body *body;
 };
 
@@ -324,7 +311,6 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
 	struct mwi_subscription *sub = mwi_data->sub;
 	struct ast_sip_endpoint *endpoint = mwi_data->endpoint;
 	pjsip_evsub_state state = mwi_data->state;
-	const char *reason = mwi_data->reason;
 	const struct ast_sip_body *body = mwi_data->body;
 	struct ast_sip_contact *contact = obj;
 	const char *state_name;
@@ -358,9 +344,6 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
 
 	sub_state = pjsip_sub_state_hdr_create(tdata->pool);
 	pj_cstr(&sub_state->sub_state, state_name);
-	if (reason) {
-		pj_cstr(&sub_state->reason_param, reason);
-	}
 	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) sub_state);
 
 	event = pjsip_event_hdr_create(tdata->pool);
@@ -374,13 +357,15 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
 	return 0;
 }
 
-static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, pjsip_evsub_state state, const char *reason,
-		struct ast_sip_body *body)
+static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
+		struct ast_sip_message_accumulator *counter)
 {
 	RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),
 				"endpoint", sub->id), ao2_cleanup);
 	char *endpoint_aors;
 	char *aor_name;
+	struct ast_sip_body body;
+	struct ast_str *body_text;
 
 	if (!endpoint) {
 		ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because endpoint does not exist\n",
@@ -393,17 +378,35 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, pjsip_evsu
 		return;
 	}
 
+	body.type = MWI_TYPE;
+	body.subtype = MWI_SUBTYPE;
+
+	body_text = ast_str_create(64);
+
+	if (!body_text) {
+		return;
+	}
+
+	if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, counter, &body_text)) {
+		ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n");
+		ast_free(body_text);
+		return;
+	}
+
+	body.body_text = ast_str_buffer(body_text);
+
 	endpoint_aors = ast_strdupa(endpoint->aors);
 
+	ast_debug(5, "Sending unsolicited MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n",
+			sub->id, counter->new_msgs, counter->old_msgs);
+
 	while ((aor_name = strsep(&endpoint_aors, ","))) {
 		RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
 		RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
 		struct unsolicited_mwi_data mwi_data = {
 			.sub = sub,
 			.endpoint = endpoint,
-			.state = state,
-			.reason = reason,
-			.body = body,
+			.body = &body,
 		};
 
 		if (!aor) {
@@ -419,63 +422,25 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, pjsip_evsu
 
 		ao2_callback(contacts, OBJ_NODATA, send_unsolicited_mwi_notify_to_contact, &mwi_data);
 	}
+
+	ast_free(body_text);
 }
 
-static void send_mwi_notify(struct mwi_subscription *sub, pjsip_evsub_state state, const char *reason)
+static void send_mwi_notify(struct mwi_subscription *sub)
 {
-	const pj_str_t *reason_str_ptr = NULL;
 	struct ast_sip_message_accumulator counter = {
 		.old_msgs = 0,
 		.new_msgs = 0,
 	};
-	RAII_VAR(struct ast_str *, body_text, ast_str_create(64), ast_free_ptr);
-	pjsip_tx_data *tdata;
-	pj_str_t reason_str;
-	struct ast_sip_body body;
-
-	body.type = sub->is_solicited ?
-		ast_sip_subscription_get_body_type(sub->sip_sub) :
-		MWI_TYPE;
-
-	body.subtype = sub->is_solicited ?
-		ast_sip_subscription_get_body_subtype(sub->sip_sub) :
-		MWI_SUBTYPE;
 
 	ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter);
 
-	if (reason) {
-		pj_cstr(&reason_str, reason);
-		reason_str_ptr = &reason_str;
-	}
-
-	if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, &counter, &body_text)) {
-		ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n");
+	if (sub->is_solicited) {
+		ast_sip_subscription_notify(sub->sip_sub, &counter, 0);
 		return;
 	}
 
-	body.body_text = ast_str_buffer(body_text);
-
-	ast_debug(5, "Sending %s MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n",
-			sub->is_solicited ? "solicited" : "unsolicited", sub->id, counter.new_msgs,
-			counter.old_msgs);
-
-	if (sub->is_solicited) {
-		if (pjsip_evsub_notify(ast_sip_subscription_get_evsub(sub->sip_sub),
-					state, NULL, reason_str_ptr, &tdata) != PJ_SUCCESS) {
-			ast_log(LOG_WARNING, "Unable to create MWI NOTIFY request to %s.\n", sub->id);
-			return;
-		}
-		if (ast_sip_add_body(tdata, &body)) {
-			ast_log(LOG_WARNING, "Unable to add body to MWI NOTIFY request\n");
-			return;
-		}
-		if (ast_sip_subscription_send_request(sub->sip_sub, tdata) != PJ_SUCCESS) {
-			ast_log(LOG_WARNING, "Unable to send MWI NOTIFY request to %s\n", sub->id);
-			return;
-		}
-	} else {
-		send_unsolicited_mwi_notify(sub, state, reason, &body);
-	}
+	send_unsolicited_mwi_notify(sub, &counter);
 }
 
 static int unsubscribe_stasis(void *obj, void *arg, int flags)
@@ -620,10 +585,9 @@ static int mwi_on_aor(void *obj, void *arg, int flags)
 }
 
 static struct mwi_subscription *mwi_create_subscription(
-	struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+	struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub)
 {
-	struct mwi_subscription *sub = mwi_subscription_alloc(
-		endpoint, AST_SIP_NOTIFIER, 1, rdata);
+	struct mwi_subscription *sub = mwi_subscription_alloc(endpoint, 1, sip_sub);
 
 	if (!sub) {
 		return NULL;
@@ -640,29 +604,23 @@ static struct mwi_subscription *mwi_create_subscription(
 }
 
 static struct mwi_subscription *mwi_subscribe_single(
-	struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *name)
+	struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub, const char *name)
 {
 	RAII_VAR(struct ast_sip_aor *, aor,
 		 ast_sip_location_retrieve_aor(name), ao2_cleanup);
 	struct mwi_subscription *sub;
 
 	if (!aor) {
+		/*! I suppose it's possible for the AOR to disappear on us
+		 * between accepting the subscription and sending the first
+		 * NOTIFY...
+		 */
 		ast_log(LOG_WARNING, "Unable to locate aor %s. MWI "
 			"subscription failed.\n", name);
 		return NULL;
 	}
 
-	if (ast_strlen_zero(aor->mailboxes)) {
-		ast_log(LOG_WARNING, "AOR %s has no configured mailboxes. "
-			"MWI subscription failed\n", name);
-		return NULL;
-	}
-
-	if (mwi_validate_for_aor(aor, endpoint, 0)) {
-		return NULL;
-	}
-
-	if (!(sub = mwi_create_subscription(endpoint, rdata))) {
+	if (!(sub = mwi_create_subscription(endpoint, sip_sub))) {
 		return NULL;
 	}
 
@@ -671,15 +629,11 @@ static struct mwi_subscription *mwi_subscribe_single(
 }
 
 static struct mwi_subscription *mwi_subscribe_all(
-	struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+	struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub)
 {
 	struct mwi_subscription *sub;
 
-	if (ast_sip_for_each_aor(endpoint->aors, mwi_validate_for_aor, endpoint)) {
-		return NULL;
-	}
-
-	sub = mwi_create_subscription(endpoint, rdata);
+	sub = mwi_create_subscription(endpoint, sip_sub);
 
 	if (!sub) {
 		return NULL;
@@ -689,106 +643,89 @@ static struct mwi_subscription *mwi_subscribe_all(
 	return sub;
 }
 
-static struct ast_sip_subscription *mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
-		pjsip_rx_data *rdata)
+static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
+		const char *resource)
 {
-	/* It's not obvious here, but the reference(s) to this subscription,
-	 * once this function exits, is held by the stasis subscription(s)
-	 * created in mwi_stasis_subscription_alloc()
-	 */
-	RAII_VAR(struct mwi_subscription *, sub, NULL, ao2_cleanup);
-	pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri;
-	pjsip_sip_uri *sip_ruri;
-	char aor_name[80];
+	struct ast_sip_aor *aor;
 
-	if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
-		ast_log(LOG_WARNING, "Attempt to SUBSCRIBE to a non-SIP URI\n");
-		return NULL;
-	}
-	sip_ruri = pjsip_uri_get_uri(ruri);
-	ast_copy_pj_str(aor_name, &sip_ruri->user, sizeof(aor_name));
-
-	/* no aor in uri? subscribe to all on endpoint */
-	if (!(sub = ast_strlen_zero(aor_name) ? mwi_subscribe_all(endpoint, rdata) :
-	      mwi_subscribe_single(endpoint, rdata, aor_name))) {
-		return NULL;
+	if (ast_strlen_zero(resource)) {
+		if (ast_sip_for_each_aor(endpoint->aors, mwi_validate_for_aor, endpoint)) {
+			return 500;
+		}
+		return 200;
 	}
 
-	ast_sip_subscription_accept(sub->sip_sub, rdata, 200);
-	send_mwi_notify(sub, PJSIP_EVSUB_STATE_ACTIVE, NULL);
+	aor = ast_sip_location_retrieve_aor(resource);
 
-	return sub->sip_sub;
-}
-
-static void mwi_resubscribe(struct ast_sip_subscription *sub,
-		pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data)
-{
-	struct mwi_subscription *mwi_sub;
-	pjsip_evsub_state state;
-	pjsip_evsub *evsub;
-	RAII_VAR(struct ast_datastore *, mwi_datastore,
-			ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup);
+	if (!aor) {
+		ast_log(LOG_WARNING, "Unable to locate aor %s. MWI "
+			"subscription failed.\n", resource);
+		return 404;
+	}
 
-	if (!mwi_datastore) {
-		return;
+	if (ast_strlen_zero(aor->mailboxes)) {
+		ast_log(LOG_WARNING, "AOR %s has no configured mailboxes. "
+			"MWI subscription failed\n", resource);
+		return 404;
 	}
 
-	mwi_sub = mwi_datastore->data;
-	evsub = ast_sip_subscription_get_evsub(sub);
-	state = pjsip_evsub_get_state(evsub);
+	if (mwi_validate_for_aor(aor, endpoint, 0)) {
+		return 500;
+	}
 
-	send_mwi_notify(mwi_sub, state, NULL);
+	return 200;
 }
 
-static void mwi_subscription_timeout(struct ast_sip_subscription *sub)
+static int mwi_initial_subscription(struct ast_sip_subscription *sip_sub)
 {
-	struct mwi_subscription *mwi_sub;
-	RAII_VAR(struct ast_datastore *, mwi_datastore,
-			ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup);
+	const char *resource = ast_sip_subscription_get_resource_name(sip_sub);
+	struct mwi_subscription *sub;
+	struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);
 
-	if (!mwi_datastore) {
-		return;
+	/* no aor in uri? subscribe to all on endpoint */
+	if (ast_strlen_zero(resource)) {
+		sub = mwi_subscribe_all(endpoint, sip_sub);
+	} else {
+		sub = mwi_subscribe_single(endpoint, sip_sub, resource);
 	}
 
+	if (!sub) {
+		ao2_cleanup(endpoint);
+		return -1;
+	}
 
-	mwi_sub = mwi_datastore->data;
-
-	ast_log(LOG_NOTICE, "MWI subscription for %s has timed out.\n", mwi_sub->id);
+	send_mwi_notify(sub);
 
-	send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_TERMINATED, "timeout");
+	ao2_cleanup(sub);
+	ao2_cleanup(endpoint);
+	return 0;
 }
 
-static void mwi_subscription_terminated(struct ast_sip_subscription *sub, pjsip_rx_data *rdata)
+static int mwi_notify_required(struct ast_sip_subscription *sip_sub,
+		enum ast_sip_subscription_notify_reason reason)
 {
 	struct mwi_subscription *mwi_sub;
-	RAII_VAR(struct ast_datastore *, mwi_datastore,
-			ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup);
-
-	if (!mwi_datastore) {
-		return;
-	}
+	struct ast_datastore *mwi_datastore;
 
-	mwi_sub = mwi_datastore->data;
+	switch (reason) {
+	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED:
+		return mwi_initial_subscription(sip_sub);
+	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED:
+	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED:
+	case AST_SIP_SUBSCRIPTION_NOTIFY_REASON_OTHER:
+		mwi_datastore = ast_sip_subscription_get_datastore(sip_sub, "MWI datastore");
 
-	ast_log(LOG_NOTICE, "MWI subscription for %s has been terminated\n", mwi_sub->id);
+		if (!mwi_datastore) {
+			return -1;
+		}
 
-	send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_TERMINATED, NULL);
-}
+		mwi_sub = mwi_datastore->data;
 
-static void mwi_notify_response(struct ast_sip_subscription *sub, pjsip_rx_data *rdata)
-{
-	/* We don't really care about NOTIFY responses for the moment */
-}
-
-static void mwi_notify_request(struct ast_sip_subscription *sub, pjsip_rx_data *rdata,
-		struct ast_sip_subscription_response_data *response_data)
-{
-	ast_log(LOG_WARNING, "Received an MWI NOTIFY request? This should not happen\n");
-}
+		send_mwi_notify(mwi_sub);
+		ao2_cleanup(mwi_datastore);
+		break;
+	}
 
-static int mwi_refresh_subscription(struct ast_sip_subscription *sub)
-{
-	ast_log(LOG_WARNING, "Being told to refresh an MWI subscription? This should not happen\n");
 	return 0;
 }
 
@@ -834,7 +771,7 @@ static int serialized_notify(void *userdata)
 {
 	struct mwi_subscription *mwi_sub = userdata;
 
-	send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_ACTIVE, NULL);
+	send_mwi_notify(mwi_sub);
 	ao2_ref(mwi_sub, -1);
 	return 0;
 }
@@ -885,7 +822,7 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags
 	}
 
 	if (endpoint->subscription.mwi.aggregate) {
-		aggregate_sub = mwi_subscription_alloc(endpoint, AST_SIP_NOTIFIER, 0, NULL);
+		aggregate_sub = mwi_subscription_alloc(endpoint, 0, NULL);
 		if (!aggregate_sub) {
 			return 0;
 		}
@@ -894,7 +831,7 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags
 	mailboxes = ast_strdupa(endpoint->subscription.mwi.mailboxes);
 	while ((mailbox = strsep(&mailboxes, ","))) {
 		struct mwi_subscription *sub = aggregate_sub ?:
-			mwi_subscription_alloc(endpoint, AST_SIP_SUBSCRIBER, 0, NULL);
+			mwi_subscription_alloc(endpoint, 0, NULL);
 		RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub,
 				mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup);
 		if (mwi_stasis_sub) {
diff --git a/res/res_pjsip_pidf_body_generator.c b/res/res_pjsip_pidf_body_generator.c
index 875a65fda485fc3414d484d52631cd21b5b50ab3..690051e136691b6ef8d21752d65486b4f41b8dc0 100644
--- a/res/res_pjsip_pidf_body_generator.c
+++ b/res/res_pjsip_pidf_body_generator.c
@@ -79,7 +79,7 @@ static int pidf_generate_body_content(void *body, void *data)
 	return 0;
 }
 
-#define MAX_STRING_GROWTHS 3
+#define MAX_STRING_GROWTHS 5
 #define XML_PROLOG 39
 
 static void pidf_to_string(void *body, struct ast_str **str)
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index dfca643bc17d315e6e46330dc76b9e5c633a3f9a..39846823e4660ce9b2e2c03059dccff1dc05affd 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -120,8 +120,8 @@ static struct pjsip_module pubsub_module = {
 	.on_rx_request = pubsub_on_rx_request,
 };
 
-#define MOD_DATA_BODY_GENERATOR "sub_body_generator"
 #define MOD_DATA_PERSISTENCE "sub_persistence"
+#define MOD_DATA_MSG "sub_msg"
 
 static const pj_str_t str_event_name = { "Event", 5 };
 
@@ -248,6 +248,55 @@ struct subscription_persistence {
 	struct timeval expires;
 };
 
+/*!
+ * \brief Real subscription details
+ *
+ * A real subscription is one that has a direct link to a
+ * PJSIP subscription and dialog.
+ */
+struct ast_sip_real_subscription {
+	/*! The underlying PJSIP event subscription structure */
+	pjsip_evsub *evsub;
+	/*! The underlying PJSIP dialog */
+	pjsip_dialog *dlg;
+};
+
+/*!
+ * \brief Virtual subscription details
+ *
+ * A virtual subscription is one that does not have a direct
+ * link to a PJSIP subscription. Instead, it is a descendent
+ * of an ast_sip_subscription. Following the ancestry will
+ * eventually lead to a real subscription.
+ */
+struct ast_sip_virtual_subscription {
+	struct ast_sip_subscription *parent;
+};
+
+/*!
+ * \brief Discriminator between real and virtual subscriptions
+ */
+enum sip_subscription_type {
+	/*!
+	 * \brief a "real" subscription.
+	 *
+	 * Real subscriptions are at the root of a tree of subscriptions.
+	 * A real subscription has a corresponding SIP subscription in the
+	 * PJSIP stack.
+	 */
+	SIP_SUBSCRIPTION_REAL,
+	/*!
+	 * \brief a "virtual" subscription.
+	 *
+	 * Virtual subscriptions are the descendents of real subscriptions
+	 * in a tree of subscriptions. Virtual subscriptions do not have
+	 * a corresponding SIP subscription in the PJSIP stack. Instead,
+	 * when a state change happens on a virtual subscription, the
+	 * state change is indicated to the virtual subscription's parent.
+	 */
+	SIP_SUBSCRIPTION_VIRTUAL,
+};
+
 /*!
  * \brief Structure representing a SIP subscription
  */
@@ -262,16 +311,23 @@ struct ast_sip_subscription {
 	const struct ast_sip_subscription_handler *handler;
 	/*! The role for this subscription */
 	enum ast_sip_subscription_role role;
-	/*! The underlying PJSIP event subscription structure */
-	pjsip_evsub *evsub;
-	/*! The underlying PJSIP dialog */
-	pjsip_dialog *dlg;
+	/*! Indicator of real or virtual subscription */
+	enum sip_subscription_type type;
+	/*! Real and virtual components of the subscription */
+	union {
+		struct ast_sip_real_subscription real;
+		struct ast_sip_virtual_subscription virtual;
+	} reality;
 	/*! Body generaator for NOTIFYs */
 	struct ast_sip_pubsub_body_generator *body_generator;
 	/*! Persistence information */
 	struct subscription_persistence *persistence;
 	/*! Next item in the list */
 	AST_LIST_ENTRY(ast_sip_subscription) next;
+	/*! List of child subscriptions */
+	AST_LIST_HEAD_NOLOCK(,ast_sip_subscription) children;
+	/*! Name of resource being subscribed to */
+	char resource[0];
 };
 
 static const char *sip_subscription_roles_map[] = {
@@ -284,6 +340,16 @@ AST_RWLIST_HEAD_STATIC(subscriptions, ast_sip_subscription);
 AST_RWLIST_HEAD_STATIC(body_generators, ast_sip_pubsub_body_generator);
 AST_RWLIST_HEAD_STATIC(body_supplements, ast_sip_pubsub_body_supplement);
 
+static pjsip_evsub *sip_subscription_get_evsub(const struct ast_sip_subscription *sub)
+{
+	return sub->reality.real.evsub;
+}
+
+static pjsip_dialog *sip_subscription_get_dlg(const struct ast_sip_subscription *sub)
+{
+	return sub->reality.real.dlg;
+}
+
 /*! \brief Destructor for subscription persistence */
 static void subscription_persistence_destroy(void *obj)
 {
@@ -310,12 +376,14 @@ static struct subscription_persistence *subscription_persistence_create(struct a
 	struct subscription_persistence *persistence = ast_sorcery_alloc(ast_sip_get_sorcery(),
 		"subscription_persistence", NULL);
 
+	pjsip_dialog *dlg = sip_subscription_get_dlg(sub);
+
 	if (!persistence) {
 		return NULL;
 	}
 
 	persistence->endpoint = ast_strdup(ast_sorcery_object_get_id(sub->endpoint));
-	ast_copy_pj_str(tag, &sub->dlg->local.info->tag, sizeof(tag));
+	ast_copy_pj_str(tag, &dlg->local.info->tag, sizeof(tag));
 	persistence->tag = ast_strdup(tag);
 
 	ast_sorcery_create(ast_sip_get_sorcery(), persistence);
@@ -326,11 +394,14 @@ static struct subscription_persistence *subscription_persistence_create(struct a
 static void subscription_persistence_update(struct ast_sip_subscription *sub,
 	pjsip_rx_data *rdata)
 {
+	pjsip_dialog *dlg;
+
 	if (!sub->persistence) {
 		return;
 	}
 
-	sub->persistence->cseq = sub->dlg->local.cseq;
+	dlg = sip_subscription_get_dlg(sub);
+	sub->persistence->cseq = dlg->local.cseq;
 
 	if (rdata) {
 		int expires;
@@ -410,13 +481,17 @@ static struct ast_sip_pubsub_body_generator *subscription_get_generator_from_rda
 		/* If a SUBSCRIBE contains no Accept headers, then we must assume that
 		 * the default accept type for the event package is to be used.
 		 */
-		ast_copy_string(accept[0], handler->default_accept, sizeof(accept[0]));
+		ast_copy_string(accept[0], handler->notifier->default_accept, sizeof(accept[0]));
 		num_accept_headers = 1;
 	}
 
 	return find_body_generator(accept, num_accept_headers);
 }
 
+static struct ast_sip_subscription *notifier_create_subscription(const struct ast_sip_subscription_handler *handler,
+		struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource,
+		struct ast_sip_pubsub_body_generator *generator);
+
 /*! \brief Callback function to perform the actual recreation of a subscription */
 static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 {
@@ -428,6 +503,10 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
 	struct ast_sip_subscription *sub;
 	struct ast_sip_pubsub_body_generator *generator;
+	int resp;
+	char *resource;
+	size_t resource_size;
+	pjsip_sip_uri *request_uri;
 
 	/* If this subscription has already expired remove it */
 	if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) {
@@ -454,6 +533,11 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 		return 0;
 	}
 
+	request_uri = pjsip_uri_get_uri(rdata.msg_info.msg->line.req.uri);
+	resource_size = pj_strlen(&request_uri->user) + 1;
+	resource = alloca(resource_size);
+	ast_copy_pj_str(resource, &request_uri->user, resource_size);
+
 	/* Update the expiration header with the new expiration */
 	expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, rdata.msg_info.msg->hdr.next);
 	if (!expires_header) {
@@ -467,7 +551,7 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 	expires_header->ivalue = (ast_tvdiff_ms(persistence->expires, ast_tvnow()) / 1000);
 
 	handler = subscription_get_handler_from_rdata(&rdata);
-	if (!handler) {
+	if (!handler || !handler->notifier) {
 		ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
 		return 0;
 	}
@@ -478,13 +562,12 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 		return 0;
 	}
 
-	ast_sip_mod_data_set(rdata.tp_info.pool, rdata.endpt_info.mod_data,
-			pubsub_module.id, MOD_DATA_BODY_GENERATOR, generator);
 	ast_sip_mod_data_set(rdata.tp_info.pool, rdata.endpt_info.mod_data,
 			pubsub_module.id, MOD_DATA_PERSISTENCE, persistence);
 
-	sub = handler->new_subscribe(endpoint, &rdata);
-	if (sub) {
+	resp = handler->notifier->new_subscribe(endpoint, resource);
+	if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
+		sub = notifier_create_subscription(handler, endpoint, &rdata, resource, generator);
 		sub->persistence = ao2_bump(persistence);
 		subscription_persistence_update(sub, &rdata);
 	} else {
@@ -596,11 +679,11 @@ static void sip_subscription_to_ami(struct ast_sip_subscription *sub,
 	ast_str_append(buf, 0, "Endpoint: %s\r\n",
 		       ast_sorcery_object_get_id(sub->endpoint));
 
-	ast_copy_pj_str(str, &sub->dlg->call_id->id, sizeof(str));
+	ast_copy_pj_str(str, &sip_subscription_get_dlg(sub)->call_id->id, sizeof(str));
 	ast_str_append(buf, 0, "Callid: %s\r\n", str);
 
 	ast_str_append(buf, 0, "State: %s\r\n", pjsip_evsub_get_state_name(
-			       ast_sip_subscription_get_evsub(sub)));
+			       sip_subscription_get_evsub(sub)));
 
 	ast_callerid_merge(str, sizeof(str),
 			   S_COR(id->self.name.valid, id->self.name.str, NULL),
@@ -653,8 +736,8 @@ static int subscription_remove_serializer(void *obj)
 	 * subscription is destroyed so that we can guarantee that our attempt to
 	 * remove the serializer will be successful.
 	 */
-	ast_sip_dialog_set_serializer(sub->dlg, NULL);
-	pjsip_dlg_dec_session(sub->dlg, &pubsub_module);
+	ast_sip_dialog_set_serializer(sip_subscription_get_dlg(sub), NULL);
+	pjsip_dlg_dec_session(sip_subscription_get_dlg(sub), &pubsub_module);
 
 	return 0;
 }
@@ -672,14 +755,14 @@ static void subscription_destructor(void *obj)
 	ao2_cleanup(sub->datastores);
 	ao2_cleanup(sub->endpoint);
 
-	if (sub->dlg) {
+	if (sip_subscription_get_dlg(sub)) {
 		ast_sip_push_task_synchronous(NULL, subscription_remove_serializer, sub);
 	}
 	ast_taskprocessor_unreference(sub->serializer);
 }
 
+
 static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event);
-static void pubsub_on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event);
 static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata,
 		int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
 static void pubsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,
@@ -690,41 +773,23 @@ static void pubsub_on_server_timeout(pjsip_evsub *sub);
 
 static pjsip_evsub_user pubsub_cb = {
 	.on_evsub_state = pubsub_on_evsub_state,
-	.on_tsx_state = pubsub_on_tsx_state,
 	.on_rx_refresh = pubsub_on_rx_refresh,
 	.on_rx_notify = pubsub_on_rx_notify,
 	.on_client_refresh = pubsub_on_client_refresh,
 	.on_server_timeout = pubsub_on_server_timeout,
 };
 
-static pjsip_evsub *allocate_evsub(const char *event, enum ast_sip_subscription_role role,
-		struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_dialog *dlg)
-{
-	pjsip_evsub *evsub;
-	/* PJSIP is kind enough to have some built-in support for certain
-	 * events. We need to use the correct initialization function for the
-	 * built-in events
-	 */
-	if (role == AST_SIP_NOTIFIER) {
-		pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &evsub);
-	} else {
-		pj_str_t pj_event;
-		pj_cstr(&pj_event, event);
-		pjsip_evsub_create_uac(dlg, &pubsub_cb, &pj_event, 0, &evsub);
-	}
-	return evsub;
-}
-
-struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
-		enum ast_sip_subscription_role role, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler,
+		struct ast_sip_endpoint *endpoint, const char *resource, enum ast_sip_subscription_role role)
 {
-	struct ast_sip_subscription *sub = ao2_alloc(sizeof(*sub), subscription_destructor);
-	pjsip_dialog *dlg;
-	struct subscription_persistence *persistence;
+	struct ast_sip_subscription *sub;
 
+	sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor);
 	if (!sub) {
 		return NULL;
 	}
+	strcpy(sub->resource, resource); /* Safe */
+
 	sub->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
 	if (!sub->datastores) {
 		ao2_ref(sub, -1);
@@ -735,28 +800,46 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
 		ao2_ref(sub, -1);
 		return NULL;
 	}
-	sub->body_generator = ast_sip_mod_data_get(rdata->endpt_info.mod_data,
-			pubsub_module.id, MOD_DATA_BODY_GENERATOR);
 	sub->role = role;
-	if (role == AST_SIP_NOTIFIER) {
-		dlg = ast_sip_create_dialog_uas(endpoint, rdata);
-	} else {
-		RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
+	sub->type = SIP_SUBSCRIPTION_REAL;
+	sub->endpoint = ao2_bump(endpoint);
+	sub->handler = handler;
 
-		contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
-		if (!contact || ast_strlen_zero(contact->uri)) {
-			ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create SIP subsription\n",
-					ast_sorcery_object_get_id(endpoint));
-			ao2_ref(sub, -1);
-			return NULL;
-		}
-		dlg = ast_sip_create_dialog_uac(endpoint, contact->uri, NULL);
+	return sub;
+}
+
+static void subscription_setup_dialog(struct ast_sip_subscription *sub, pjsip_dialog *dlg)
+{
+	/* We keep a reference to the dialog until our subscription is destroyed. See
+	 * the subscription_destructor for more details
+	 */
+	pjsip_dlg_inc_session(dlg, &pubsub_module);
+	sub->reality.real.dlg = dlg;
+	ast_sip_dialog_set_serializer(dlg, sub->serializer);
+	pjsip_evsub_set_mod_data(sip_subscription_get_evsub(sub), pubsub_module.id, sub);
+}
+
+static struct ast_sip_subscription *notifier_create_subscription(const struct ast_sip_subscription_handler *handler,
+		struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource,
+		struct ast_sip_pubsub_body_generator *generator)
+{
+	struct ast_sip_subscription *sub;
+	pjsip_dialog *dlg;
+	struct subscription_persistence *persistence;
+
+	sub = allocate_subscription(handler, endpoint, resource, AST_SIP_NOTIFIER);
+	if (!sub) {
+		return NULL;
 	}
+
+	sub->body_generator = generator;
+	dlg = ast_sip_create_dialog_uas(endpoint, rdata);
 	if (!dlg) {
 		ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
 		ao2_ref(sub, -1);
 		return NULL;
 	}
+
 	persistence = ast_sip_mod_data_get(rdata->endpt_info.mod_data,
 			pubsub_module.id, MOD_DATA_PERSISTENCE);
 	if (persistence) {
@@ -768,62 +851,102 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
 		dlg->local.cseq = persistence->cseq;
 		dlg->remote.cseq = persistence->cseq;
 	}
-	sub->evsub = allocate_evsub(handler->event_name, role, endpoint, rdata, dlg);
-	/* We keep a reference to the dialog until our subscription is destroyed. See
-	 * the subscription_destructor for more details
-	 */
-	pjsip_dlg_inc_session(dlg, &pubsub_module);
-	sub->dlg = dlg;
-	ast_sip_dialog_set_serializer(dlg, sub->serializer);
-	pjsip_evsub_set_mod_data(sub->evsub, pubsub_module.id, sub);
-	ao2_ref(endpoint, +1);
-	sub->endpoint = endpoint;
-	sub->handler = handler;
+
+	pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub->reality.real.evsub);
+	subscription_setup_dialog(sub, dlg);
+
+	ast_sip_mod_data_set(dlg->pool, dlg->mod_data, pubsub_module.id, MOD_DATA_MSG,
+			pjsip_msg_clone(dlg->pool, rdata->msg_info.msg));
 
 	add_subscription(sub);
 	return sub;
 }
 
-struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
+void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header)
 {
-	ast_assert(sub->endpoint != NULL);
-	ao2_ref(sub->endpoint, +1);
-	return sub->endpoint;
-}
+	pjsip_dialog *dlg = sip_subscription_get_dlg(sub);
+	pjsip_msg *msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
+	pj_str_t name;
 
-struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub)
-{
-	ast_assert(sub->serializer != NULL);
-	return sub->serializer;
+	pj_cstr(&name, header);
+
+	return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
 }
 
-pjsip_evsub *ast_sip_subscription_get_evsub(struct ast_sip_subscription *sub)
+struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
+		struct ast_sip_endpoint *endpoint, const char *resource)
 {
-	return sub->evsub;
+	struct ast_sip_subscription *sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor);
+	pjsip_dialog *dlg;
+	struct ast_sip_contact *contact;
+	pj_str_t event;
+	pjsip_tx_data *tdata;
+	pjsip_evsub *evsub;
+
+	sub = allocate_subscription(handler, endpoint, resource, AST_SIP_SUBSCRIBER);
+	if (!sub) {
+		return NULL;
+	}
+
+	contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
+	if (!contact || ast_strlen_zero(contact->uri)) {
+		ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create SIP subsription\n",
+				ast_sorcery_object_get_id(endpoint));
+		ao2_ref(sub, -1);
+		ao2_cleanup(contact);
+		return NULL;
+	}
+
+	dlg = ast_sip_create_dialog_uac(endpoint, contact->uri, NULL);
+	ao2_cleanup(contact);
+	if (!dlg) {
+		ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
+		ao2_ref(sub, -1);
+		return NULL;
+	}
+
+	pj_cstr(&event, handler->event_name);
+	pjsip_evsub_create_uac(dlg, &pubsub_cb, &event, 0, &sub->reality.real.evsub);
+	subscription_setup_dialog(sub, dlg);
+
+	add_subscription(sub);
+
+	evsub = sip_subscription_get_evsub(sub);
+
+	if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
+		pjsip_evsub_send_request(evsub, tdata);
+	} else {
+		/* pjsip_evsub_terminate will result in pubsub_on_evsub_state,
+		 * being called and terminating the subscription. Therefore, we don't
+		 * need to decrease the reference count of sub here.
+		 */
+		pjsip_evsub_terminate(evsub, PJ_TRUE);
+		return NULL;
+	}
+
+	return sub;
 }
 
-pjsip_dialog *ast_sip_subscription_get_dlg(struct ast_sip_subscription *sub)
+struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
 {
-	return sub->dlg;
+	ast_assert(sub->endpoint != NULL);
+	ao2_ref(sub->endpoint, +1);
+	return sub->endpoint;
 }
 
-int ast_sip_subscription_accept(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, int response)
+struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub)
 {
-	/* If this is a persistence recreation the subscription has already been accepted */
-	if (ast_sip_mod_data_get(rdata->endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE)) {
-		return 0;
-	}
-
-	return pjsip_evsub_accept(ast_sip_subscription_get_evsub(sub), rdata, response, NULL) == PJ_SUCCESS ? 0 : -1;
+	ast_assert(sub->serializer != NULL);
+	return sub->serializer;
 }
 
-int ast_sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx_data *tdata)
+static int sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx_data *tdata)
 {
 	struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sub);
 	int res;
 
 	ao2_ref(sub, +1);
-	res = pjsip_evsub_send_request(ast_sip_subscription_get_evsub(sub),
+	res = pjsip_evsub_send_request(sip_subscription_get_evsub(sub),
 			tdata) == PJ_SUCCESS ? 0 : -1;
 
 	subscription_persistence_update(sub, NULL);
@@ -831,7 +954,7 @@ int ast_sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx
 	ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
 		"StateText: %s\r\n"
 		"Endpoint: %s\r\n",
-		pjsip_evsub_get_state_name(ast_sip_subscription_get_evsub(sub)),
+		pjsip_evsub_get_state_name(sip_subscription_get_evsub(sub)),
 		ast_sorcery_object_get_id(endpoint));
 	ao2_cleanup(sub);
 	ao2_cleanup(endpoint);
@@ -839,6 +962,83 @@ int ast_sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx
 	return res;
 }
 
+int ast_sip_subscription_notify(struct ast_sip_subscription *sub, void *notify_data,
+		int terminate)
+{
+	struct ast_sip_body body = {
+		.type = ast_sip_subscription_get_body_type(sub),
+		.subtype = ast_sip_subscription_get_body_subtype(sub),
+	};
+	struct ast_str *body_text = ast_str_create(64);
+	pjsip_evsub *evsub = sip_subscription_get_evsub(sub);
+	pjsip_tx_data *tdata;
+	pjsip_evsub_state state;
+
+	if (!body_text) {
+		return -1;
+	}
+
+	if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, notify_data, &body_text)) {
+		ast_free(body_text);
+		return -1;
+	}
+
+	body.body_text = ast_str_buffer(body_text);
+
+	if (terminate) {
+		state = PJSIP_EVSUB_STATE_TERMINATED;
+	} else {
+		state = pjsip_evsub_get_state(evsub) <= PJSIP_EVSUB_STATE_ACTIVE ?
+			PJSIP_EVSUB_STATE_ACTIVE : PJSIP_EVSUB_STATE_TERMINATED;
+	}
+
+	ast_log_backtrace();
+
+	if (pjsip_evsub_notify(evsub, state, NULL, NULL, &tdata) != PJ_SUCCESS) {
+		ast_free(body_text);
+		return -1;
+	}
+	if (ast_sip_add_body(tdata, &body)) {
+		ast_free(body_text);
+		pjsip_tx_data_dec_ref(tdata);
+		return -1;
+	}
+	if (sip_subscription_send_request(sub, tdata)) {
+		ast_free(body_text);
+		pjsip_tx_data_dec_ref(tdata);
+		return -1;
+	}
+
+	return 0;
+}
+
+void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
+{
+	pjsip_dialog *dlg = sip_subscription_get_dlg(sub);
+	ast_copy_pj_str(buf, &dlg->local.info_str, size);
+}
+
+void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
+{
+	pjsip_dialog *dlg = sip_subscription_get_dlg(sub);
+	ast_copy_pj_str(buf, &dlg->remote.info_str, size);
+}
+
+const char *ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub)
+{
+	return sub->resource;
+}
+
+static int sip_subscription_accept(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, int response)
+{
+	/* If this is a persistence recreation the subscription has already been accepted */
+	if (ast_sip_mod_data_get(rdata->endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE)) {
+		return 0;
+	}
+
+	return pjsip_evsub_accept(sip_subscription_get_evsub(sub), rdata, response, NULL) == PJ_SUCCESS ? 0 : -1;
+}
+
 static void subscription_datastore_destroy(void *obj)
 {
 	struct ast_datastore *datastore = obj;
@@ -1019,9 +1219,9 @@ static struct ast_sip_subscription_handler *find_sub_handler_for_event_name(cons
 int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *handler)
 {
 	pj_str_t event;
-	pj_str_t accept[AST_SIP_MAX_ACCEPT];
+	pj_str_t accept[AST_SIP_MAX_ACCEPT] = { {0, }, };
 	struct ast_sip_subscription_handler *existing;
-	int i;
+	int i = 0;
 
 	if (ast_strlen_zero(handler->event_name)) {
 		ast_log(LOG_ERROR, "No event package specified for subscription handler. Cannot register\n");
@@ -1117,6 +1317,11 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
 	struct ast_sip_subscription *sub;
 	struct ast_sip_pubsub_body_generator *generator;
+	char *resource;
+	pjsip_uri *request_uri;
+	pjsip_sip_uri *request_uri_sip;
+	size_t resource_size;
+	int resp;
 
 	endpoint = ast_pjsip_rdata_get_endpoint(rdata);
 	ast_assert(endpoint != NULL);
@@ -1127,6 +1332,22 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 		return PJ_TRUE;
 	}
 
+	request_uri = rdata->msg_info.msg->line.req.uri;
+
+	if (!PJSIP_URI_SCHEME_IS_SIP(request_uri) && !PJSIP_URI_SCHEME_IS_SIPS(request_uri)) {
+		char uri_str[PJSIP_MAX_URL_SIZE];
+
+		pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str, sizeof(uri_str));
+		ast_log(LOG_WARNING, "Request URI '%s' is not a sip: or sips: URI.\n", uri_str);
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
+		return PJ_TRUE;
+	}
+
+	request_uri_sip = pjsip_uri_get_uri(request_uri);
+	resource_size = pj_strlen(&request_uri_sip->user) + 1;
+	resource = alloca(resource_size);
+	ast_copy_pj_str(resource, &request_uri_sip->user, resource_size);
+
 	expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, rdata->msg_info.msg->hdr.next);
 
 	if (expires_header) {
@@ -1142,7 +1363,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 423, NULL, NULL, NULL);
 			return PJ_TRUE;
 		}
-        }
+	}
 
 	handler = subscription_get_handler_from_rdata(rdata);
 	if (!handler) {
@@ -1156,27 +1377,22 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 		return PJ_TRUE;
 	}
 
-	ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,
-			pubsub_module.id, MOD_DATA_BODY_GENERATOR, generator);
+	resp = handler->notifier->new_subscribe(endpoint, resource);
+	if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
+		return PJ_TRUE;
+	}
 
-	sub = handler->new_subscribe(endpoint, rdata);
+	sub = notifier_create_subscription(handler, endpoint, rdata, resource, generator);
 	if (!sub) {
-		pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
-
-		if (trans) {
-			pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
-			pjsip_tx_data *tdata;
-
-			if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, &tdata) != PJ_SUCCESS) {
-				return PJ_TRUE;
-			}
-			pjsip_dlg_send_response(dlg, trans, tdata);
-		} else {
-			pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
-		}
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
 	} else {
 		sub->persistence = subscription_persistence_create(sub);
 		subscription_persistence_update(sub, rdata);
+		sip_subscription_accept(sub, rdata, resp);
+		if (handler->notifier->notify_required(sub, AST_SIP_SUBSCRIPTION_NOTIFY_REASON_STARTED)) {
+			pjsip_evsub_terminate(sip_subscription_get_evsub(sub), PJ_TRUE);
+		}
 	}
 
 	return PJ_TRUE;
@@ -1229,10 +1445,114 @@ static enum sip_publish_type determine_sip_publish_type(pjsip_rx_data *rdata,
 	return SIP_PUBLISH_UNKNOWN;
 }
 
+/*! \brief Internal destructor for publications */
+static void publication_destroy_fn(void *obj)
+{
+	struct ast_sip_publication *publication = obj;
+
+	ast_debug(3, "Destroying SIP publication\n");
+
+	ao2_cleanup(publication->datastores);
+	ao2_cleanup(publication->endpoint);
+}
+
+static struct ast_sip_publication *sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+{
+	struct ast_sip_publication *publication;
+	pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
+
+	ast_assert(endpoint != NULL);
+
+	if (!(publication = ao2_alloc(sizeof(*publication), publication_destroy_fn))) {
+		return NULL;
+	}
+
+	if (!(publication->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp))) {
+		ao2_ref(publication, -1);
+		return NULL;
+	}
+
+	publication->entity_tag = ast_atomic_fetchadd_int(&esc_etag_counter, +1);
+	ao2_ref(endpoint, +1);
+	publication->endpoint = endpoint;
+	publication->expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
+	publication->sched_id = -1;
+
+	return publication;
+}
+
+static int sip_publication_respond(struct ast_sip_publication *pub, int status_code,
+		pjsip_rx_data *rdata)
+{
+	pj_status_t status;
+	pjsip_tx_data *tdata;
+	pjsip_transaction *tsx;
+
+	if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, status_code, NULL, &tdata) != PJ_SUCCESS) {
+		return -1;
+	}
+
+	if (PJSIP_IS_STATUS_IN_CLASS(status_code, 200)) {
+		RAII_VAR(char *, entity_tag, NULL, ast_free_ptr);
+		RAII_VAR(char *, expires, NULL, ast_free_ptr);
+
+		if ((ast_asprintf(&entity_tag, "%d", pub->entity_tag) < 0) ||
+			(ast_asprintf(&expires, "%d", pub->expires) < 0)) {
+			pjsip_tx_data_dec_ref(tdata);
+			return -1;
+		}
+
+		ast_sip_add_header(tdata, "SIP-ETag", entity_tag);
+		ast_sip_add_header(tdata, "Expires", expires);
+	}
+
+	if ((status = pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx)) != PJ_SUCCESS) {
+		return -1;
+	}
+
+	pjsip_tsx_recv_msg(tsx, rdata);
+
+	if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) {
+		return -1;
+	}
+
+	return 0;
+}
+
 static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata,
 	struct ast_sip_publish_handler *handler)
 {
-	struct ast_sip_publication *publication = handler->new_publication(endpoint, rdata);
+	struct ast_sip_publication *publication;
+	char *resource;
+	size_t resource_size;
+	pjsip_uri *request_uri;
+	pjsip_sip_uri *request_uri_sip;
+	int resp;
+
+	request_uri = rdata->msg_info.msg->line.req.uri;
+
+	if (!PJSIP_URI_SCHEME_IS_SIP(request_uri) && !PJSIP_URI_SCHEME_IS_SIPS(request_uri)) {
+		char uri_str[PJSIP_MAX_URL_SIZE];
+
+		pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str, sizeof(uri_str));
+		ast_log(LOG_WARNING, "Request URI '%s' is not a sip: or sips: URI.\n", uri_str);
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
+		return NULL;
+	}
+
+	request_uri_sip = pjsip_uri_get_uri(request_uri);
+	resource_size = pj_strlen(&request_uri_sip->user) + 1;
+	resource = alloca(resource_size);
+	ast_copy_pj_str(resource, &request_uri_sip->user, resource_size);
+
+	resp = handler->new_publication(endpoint, resource);
+
+	if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
+		return NULL;
+	}
+
+	publication = sip_create_publication(endpoint, rdata);
 
 	if (!publication) {
 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL, NULL, NULL);
@@ -1240,6 +1560,14 @@ static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoi
 	}
 
 	publication->handler = handler;
+	if (publication->handler->publication_state_change(publication, rdata->msg_info.msg->body,
+			AST_SIP_PUBLISH_STATE_INITIALIZED)) {
+		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
+		ao2_cleanup(publication);
+		return NULL;
+	}
+
+	sip_publication_respond(publication, resp, rdata);
 
 	return publication;
 }
@@ -1321,14 +1649,19 @@ static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata)
 			publication = publish_request_initial(endpoint, rdata, handler);
 			break;
 		case SIP_PUBLISH_REFRESH:
+			sip_publication_respond(publication, 200, rdata);
 		case SIP_PUBLISH_MODIFY:
-			if (handler->publish_refresh(publication, rdata)) {
+			if (handler->publication_state_change(publication, rdata->msg_info.msg->body,
+						AST_SIP_PUBLISH_STATE_ACTIVE)) {
 				/* If an error occurs we want to terminate the publication */
 				expires = 0;
 			}
+			sip_publication_respond(publication, 200, rdata);
 			break;
 		case SIP_PUBLISH_REMOVE:
-			handler->publish_termination(publication, rdata);
+			handler->publication_state_change(publication, rdata->msg_info.msg->body,
+					AST_SIP_PUBLISH_STATE_TERMINATED);
+			sip_publication_respond(publication, 200, rdata);
 			break;
 		case SIP_PUBLISH_UNKNOWN:
 		default:
@@ -1350,85 +1683,11 @@ static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata)
 	return PJ_TRUE;
 }
 
-/*! \brief Internal destructor for publications */
-static void publication_destroy_fn(void *obj)
-{
-	struct ast_sip_publication *publication = obj;
-
-	ast_debug(3, "Destroying SIP publication\n");
-
-	ao2_cleanup(publication->datastores);
-	ao2_cleanup(publication->endpoint);
-}
-
-struct ast_sip_publication *ast_sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
-{
-	struct ast_sip_publication *publication;
-	pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
-
-	ast_assert(endpoint != NULL);
-
-	if (!(publication = ao2_alloc(sizeof(*publication), publication_destroy_fn))) {
-		return NULL;
-	}
-
-	if (!(publication->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp))) {
-		ao2_ref(publication, -1);
-		return NULL;
-	}
-
-	publication->entity_tag = ast_atomic_fetchadd_int(&esc_etag_counter, +1);
-	ao2_ref(endpoint, +1);
-	publication->endpoint = endpoint;
-	publication->expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
-	publication->sched_id = -1;
-
-	return publication;
-}
-
 struct ast_sip_endpoint *ast_sip_publication_get_endpoint(struct ast_sip_publication *pub)
 {
 	return pub->endpoint;
 }
 
-int ast_sip_publication_create_response(struct ast_sip_publication *pub, int status_code, pjsip_rx_data *rdata,
-	pjsip_tx_data **tdata)
-{
-	if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, status_code, NULL, tdata) != PJ_SUCCESS) {
-		return -1;
-	}
-
-	if (PJSIP_IS_STATUS_IN_CLASS(status_code, 200)) {
-		RAII_VAR(char *, entity_tag, NULL, ast_free_ptr);
-		RAII_VAR(char *, expires, NULL, ast_free_ptr);
-
-		if ((ast_asprintf(&entity_tag, "%d", pub->entity_tag) < 0) ||
-			(ast_asprintf(&expires, "%d", pub->expires) < 0)) {
-			pjsip_tx_data_dec_ref(*tdata);
-			return -1;
-		}
-
-		ast_sip_add_header(*tdata, "SIP-ETag", entity_tag);
-		ast_sip_add_header(*tdata, "Expires", expires);
-	}
-
-	return 0;
-}
-
-pj_status_t ast_sip_publication_send_response(struct ast_sip_publication *pub, pjsip_rx_data *rdata,
-	pjsip_tx_data *tdata)
-{
-	pj_status_t status;
-	pjsip_transaction *tsx;
-
-	if ((status = pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx)) != PJ_SUCCESS) {
-		return status;
-	}
-
-	pjsip_tsx_recv_msg(tsx, rdata);
-
-	return pjsip_tsx_send_msg(tsx, tdata);
-}
 
 int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator *generator)
 {
@@ -1590,123 +1849,53 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
 	pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
 }
 
-static void pubsub_on_tsx_state(pjsip_evsub *evsub, pjsip_transaction *tsx, pjsip_event *event)
-{
-	struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
-
-	if (!sub) {
-		return;
-	}
-
-	if (sub->handler->notify_response && tsx->role == PJSIP_ROLE_UAC &&
-	    event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
-		sub->handler->notify_response(sub, event->body.tsx_state.src.rdata);
-	}
-}
-
-static void set_parameters_from_response_data(pj_pool_t *pool, int *p_st_code,
-		pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body,
-		struct ast_sip_subscription_response_data *response_data)
-{
-	ast_assert(response_data->status_code >= 200 && response_data->status_code <= 699);
-	*p_st_code = response_data->status_code;
-
-	if (!ast_strlen_zero(response_data->status_text)) {
-		pj_strdup2(pool, *p_st_text, response_data->status_text);
-	}
-
-	if (response_data->headers) {
-		struct ast_variable *iter;
-		for (iter = response_data->headers; iter; iter = iter->next) {
-			pj_str_t header_name;
-			pj_str_t header_value;
-			pjsip_generic_string_hdr *hdr;
-
-			pj_cstr(&header_name, iter->name);
-			pj_cstr(&header_value, iter->value);
-			hdr = pjsip_generic_string_hdr_create(pool, &header_name, &header_value);
-			pj_list_insert_before(res_hdr, hdr);
-		}
-	}
-
-	if (response_data->body) {
-		pj_str_t type;
-		pj_str_t subtype;
-		pj_str_t body_text;
-
-		pj_cstr(&type, response_data->body->type);
-		pj_cstr(&subtype, response_data->body->subtype);
-		pj_cstr(&body_text, response_data->body->body_text);
-
-		*p_body = pjsip_msg_body_create(pool, &type, &subtype, &body_text);
-	}
-}
-
-static int response_data_changed(struct ast_sip_subscription_response_data *response_data)
-{
-	if (response_data->status_code != 200 ||
-			!ast_strlen_zero(response_data->status_text) ||
-			response_data->headers ||
-			response_data->body) {
-		return 1;
-	}
-	return 0;
-}
-
 static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
 		int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
 {
 	struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
-	struct ast_sip_subscription_response_data response_data = {
-		.status_code = 200,
-	};
+	enum ast_sip_subscription_notify_reason reason;
 
 	if (!sub) {
 		return;
 	}
 
-	if (pjsip_evsub_get_state(sub->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
-		sub->handler->subscription_terminated(sub, rdata);
-		return;
+	if (pjsip_evsub_get_state(sip_subscription_get_evsub(sub)) == PJSIP_EVSUB_STATE_TERMINATED) {
+		reason = AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED;
+	} else {
+		reason = AST_SIP_SUBSCRIPTION_NOTIFY_REASON_RENEWED;
 	}
-
-	sub->handler->resubscribe(sub, rdata, &response_data);
-
-	if (!response_data_changed(&response_data)) {
-		return;
+	if (sub->handler->notifier->notify_required(sub, reason)) {
+		*p_st_code = 500;
 	}
-
-	set_parameters_from_response_data(rdata->tp_info.pool, p_st_code, p_st_text,
-			res_hdr, p_body, &response_data);
 }
 
 static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
 		pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
 {
 	struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
-	struct ast_sip_subscription_response_data response_data = {
-		.status_code = 200,
-	};
-
-	if (!sub || !sub->handler->notify_request) {
-		return;
-	}
-
-	sub->handler->notify_request(sub, rdata, &response_data);
 
-	if (!response_data_changed(&response_data)) {
+	if (!sub) {
 		return;
 	}
 
-	set_parameters_from_response_data(rdata->tp_info.pool, p_st_code, p_st_text,
-			res_hdr, p_body, &response_data);
+	sub->handler->subscriber->state_change(sub, rdata->msg_info.msg->body,
+			pjsip_evsub_get_state(evsub));
 }
 
 static int serialized_pubsub_on_client_refresh(void *userdata)
 {
 	struct ast_sip_subscription *sub = userdata;
+	pjsip_evsub *evsub;
+	pjsip_tx_data *tdata;
+
+	evsub = sip_subscription_get_evsub(sub);
 
-	sub->handler->refresh_subscription(sub);
+	if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
+		pjsip_evsub_send_request(evsub, tdata);
+	} else {
+		pjsip_evsub_terminate(evsub, PJ_TRUE);
+		return 0;
+	}
 	ao2_cleanup(sub);
 	return 0;
 }
@@ -1723,7 +1912,9 @@ static int serialized_pubsub_on_server_timeout(void *userdata)
 {
 	struct ast_sip_subscription *sub = userdata;
 
-	sub->handler->subscription_timeout(sub);
+	sub->handler->notifier->notify_required(sub,
+			AST_SIP_SUBSCRIPTION_NOTIFY_REASON_TERMINATED);
+
 	ao2_cleanup(sub);
 	return 0;
 }
diff --git a/res/res_pjsip_pubsub.exports.in b/res/res_pjsip_pubsub.exports.in
index 0877d5c6cba8b911d5ff539de44b9067284f8e7c..ca165af927600e49f94985867cb5214727907e84 100644
--- a/res/res_pjsip_pubsub.exports.in
+++ b/res/res_pjsip_pubsub.exports.in
@@ -1,7 +1,7 @@
 {
 	global:
 		LINKER_SYMBOL_PREFIXast_sip_create_subscription;
-		LINKER_SYMBOL_PREFIXast_sip_subsription_get_endpoint;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_get_endpoint;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_serializer;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_evsub;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_dlg;
@@ -30,6 +30,11 @@
 		LINKER_SYMBOL_PREFIXast_sip_pubsub_generate_body_content;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_body_type;
 		LINKER_SYMBOL_PREFIXast_sip_subscription_get_body_subtype;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_get_resource_name;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_notify;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_get_local_uri;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_get_remote_uri;
+		LINKER_SYMBOL_PREFIXast_sip_subscription_get_header;
 	local:
 		*;
 };
diff --git a/res/res_pjsip_xpidf_body_generator.c b/res/res_pjsip_xpidf_body_generator.c
index fba6152b3cd6606a7de458ba4d9fe9fe56ad59e6..aeb313f12dc34f502c350962c1c8721b20158b27 100644
--- a/res/res_pjsip_xpidf_body_generator.c
+++ b/res/res_pjsip_xpidf_body_generator.c
@@ -96,7 +96,7 @@ static int xpidf_generate_body_content(void *body, void *data)
 	return 0;
 }
 
-#define MAX_STRING_GROWTHS 3
+#define MAX_STRING_GROWTHS 5
 #define XML_PROLOG 39
 
 static void xpidf_to_string(void *body, struct ast_str **str)