diff --git a/conf/rayo/autoload_configs/rayo.conf.xml b/conf/rayo/autoload_configs/rayo.conf.xml
index 45a8ab04bb..52a1a35977 100644
--- a/conf/rayo/autoload_configs/rayo.conf.xml
+++ b/conf/rayo/autoload_configs/rayo.conf.xml
@@ -109,6 +109,9 @@
 
 	<!-- IQ request aliases.  Used mainly for testing purposes or for controlling a rayo call via the console -->
 	<aliases>
+		<alias name="detect" target="call" args="1"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:$1:1"/></input>]]></alias>
+		<alias name="detect-once" target="call" args="1"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:$1:1?terminate=true"/></input>]]></alias>
+		<alias name="detect-tones" target="call"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:busy:1"/><grammar url="urn:xmpp:rayo:cpa:congestion:1"/><grammar url="urn:xmpp:rayo:cpa:sit:1"/></input>]]></alias>
 		<alias name="ping" target="external"><![CDATA[<iq type="get"><ping xmlns="urn:xmpp:ping"/></iq>]]></alias>
 		<alias name="dial" target="server" args="2"><![CDATA[<dial xmlns="urn:xmpp:rayo:1" from="$1" to="$2"/>]]></alias>
 		<alias name="answer" target="call"><![CDATA[<answer xmlns="urn:xmpp:rayo:1"/>]]></alias>
diff --git a/src/mod/event_handlers/mod_rayo/Makefile b/src/mod/event_handlers/mod_rayo/Makefile
index 3b7e5dba3f..070f41f98c 100644
--- a/src/mod/event_handlers/mod_rayo/Makefile
+++ b/src/mod/event_handlers/mod_rayo/Makefile
@@ -10,6 +10,7 @@ LOCAL_OBJS= $(IKS_LA) \
 	iks_helpers.o \
 	nlsml.o \
 	rayo_components.o \
+	rayo_cpa_component.o \
 	rayo_cpa_detector.o \
 	rayo_elements.o \
 	rayo_fax_components.o \
@@ -24,6 +25,7 @@ LOCAL_SOURCES=	\
 	iks_helpers.c \
 	nlsml.c \
 	rayo_components.c \
+	rayo_cpa_component.c \
 	rayo_cpa_detector.c \
 	rayo_elements.c \
 	rayo_fax_components.c \
diff --git a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml
index 45a8ab04bb..52a1a35977 100644
--- a/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml
+++ b/src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml
@@ -109,6 +109,9 @@
 
 	<!-- IQ request aliases.  Used mainly for testing purposes or for controlling a rayo call via the console -->
 	<aliases>
+		<alias name="detect" target="call" args="1"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:$1:1"/></input>]]></alias>
+		<alias name="detect-once" target="call" args="1"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:$1:1?terminate=true"/></input>]]></alias>
+		<alias name="detect-tones" target="call"><![CDATA[<input xmlns="urn:xmpp:rayo:input:1" mode="cpa"><grammar url="urn:xmpp:rayo:cpa:busy:1"/><grammar url="urn:xmpp:rayo:cpa:congestion:1"/><grammar url="urn:xmpp:rayo:cpa:sit:1"/></input>]]></alias>
 		<alias name="ping" target="external"><![CDATA[<iq type="get"><ping xmlns="urn:xmpp:ping"/></iq>]]></alias>
 		<alias name="dial" target="server" args="2"><![CDATA[<dial xmlns="urn:xmpp:rayo:1" from="$1" to="$2"/>]]></alias>
 		<alias name="answer" target="call"><![CDATA[<answer xmlns="urn:xmpp:rayo:1"/>]]></alias>
diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c
index c650e66877..95162093c8 100644
--- a/src/mod/event_handlers/mod_rayo/mod_rayo.c
+++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c
@@ -1191,6 +1191,10 @@ static struct rayo_mixer *_rayo_mixer_create(const char *name, const char *file,
  */
 static void rayo_component_cleanup(struct rayo_actor *actor)
 {
+	if (RAYO_COMPONENT(actor)->cleanup_fn) {
+		RAYO_COMPONENT(actor)->cleanup_fn(actor);
+	}
+
 	/* parent can now be destroyed */
 	RAYO_UNLOCK(RAYO_COMPONENT(actor)->parent);
 }
@@ -1202,9 +1206,12 @@ static void rayo_component_cleanup(struct rayo_actor *actor)
  * @param id internal ID of this component
  * @param parent the parent that owns this component
  * @param client_jid the client that created this component
+ * @param cleanup optional cleanup function
+ * @param file file that called this function
+ * @param line line number that called this function
  * @return the component
  */
-struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, const char *file, int line)
+struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, rayo_actor_cleanup_fn cleanup, const char *file, int line)
 {
 	char *ref = switch_mprintf("%s-%d", subtype, rayo_actor_seq_next(parent));
 	char *jid = switch_mprintf("%s/%s", RAYO_JID(parent), ref);
@@ -1218,6 +1225,7 @@ struct rayo_component *_rayo_component_init(struct rayo_component *component, sw
 	component->client_jid = switch_core_strdup(pool, client_jid);
 	component->ref = switch_core_strdup(pool, ref);
 	component->parent = parent;
+	component->cleanup_fn = cleanup;
 
 	switch_safe_free(ref);
 	switch_safe_free(jid);
@@ -2443,6 +2451,8 @@ static iks *on_iq_get_xmpp_disco(struct rayo_actor *server, struct rayo_message
 	feature = iks_insert(x, "feature");
 	iks_insert_attrib(feature, "var", RAYO_NS);
 	feature = iks_insert(x, "feature");
+	iks_insert_attrib(feature, "var", RAYO_CPA_NS);
+	feature = iks_insert(x, "feature");
 	iks_insert_attrib(feature, "var", RAYO_FAX_NS);
 
 	/* TODO The response MUST also include features for the application formats and transport methods supported by
diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.h b/src/mod/event_handlers/mod_rayo/mod_rayo.h
index b862802f31..9d0bf05490 100644
--- a/src/mod/event_handlers/mod_rayo/mod_rayo.h
+++ b/src/mod/event_handlers/mod_rayo/mod_rayo.h
@@ -42,6 +42,9 @@
 #define RAYO_CALL_NS RAYO_BASE "call:" RAYO_VERSION
 #define RAYO_MIXER_NS RAYO_BASE "mixer:" RAYO_VERSION
 
+#define RAYO_CPA_BASE RAYO_BASE "cpa:"
+#define RAYO_CPA_NS RAYO_CPA_BASE RAYO_VERSION
+
 #define RAT_CALL "CALL"
 #define RAT_COMPONENT "COMPONENT"
 #define RAT_CALL_COMPONENT RAT_COMPONENT"_CALL"
@@ -119,6 +122,8 @@ struct rayo_component {
 	const char *client_jid;
 	/** external ref */
 	const char *ref;
+	/** optional cleanup */
+	rayo_actor_cleanup_fn cleanup_fn;
 };
 
 #define RAYO_ACTOR(x) ((struct rayo_actor *)x)
@@ -160,8 +165,9 @@ extern const char *rayo_call_get_dcp_jid(struct rayo_call *call);
 
 #define rayo_mixer_get_name(mixer) RAYO_ID(mixer)
 
-#define rayo_component_init(component, pool, type, subtype, id, parent, client_jid) _rayo_component_init(component, pool, type, subtype, id, parent, client_jid, __FILE__, __LINE__)
-extern struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, const char *file, int line);
+#define rayo_component_init(component, pool, type, subtype, id, parent, client_jid) _rayo_component_init(component, pool, type, subtype, id, parent, client_jid, NULL, __FILE__, __LINE__)
+#define rayo_component_init_cleanup(component, pool, type, subtype, id, parent, client_jid, cleanup) _rayo_component_init(component, pool, type, subtype, id, parent, client_jid, cleanup, __FILE__, __LINE__)
+extern struct rayo_component *_rayo_component_init(struct rayo_component *component, switch_memory_pool_t *pool, const char *type, const char *subtype, const char *id, struct rayo_actor *parent, const char *client_jid, rayo_actor_cleanup_fn cleanup, const char *file, int line);
 extern switch_bool_t is_component_actor(struct rayo_actor *);
 
 typedef iks *(*rayo_actor_xmpp_handler)(struct rayo_actor *, struct rayo_message *, void *);
diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_component.c b/src/mod/event_handlers/mod_rayo/rayo_cpa_component.c
new file mode 100644
index 0000000000..8004347118
--- /dev/null
+++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_component.c
@@ -0,0 +1,401 @@
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2014, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * rayo_cpa_component.c -- input component "cpa" mode implementation
+ */
+#include <switch.h>
+
+#include "rayo_cpa_component.h"
+#include "mod_rayo.h"
+#include "rayo_components.h"
+#include "rayo_cpa_detector.h"
+
+/**
+ * Module globals
+ */
+struct {
+	/** signal subscribers */
+	switch_hash_t *subscribers;
+	/** synchronizes access to subscribers */
+	switch_mutex_t *subscribers_mutex;
+	/** module pool */
+	switch_memory_pool_t *pool;
+} globals;
+
+/**
+ * A CPA signal monitored by this component
+ */
+struct cpa_signal {
+	/** name of this signal */
+	const char *name;
+	/** true if signal causes component termination */
+	int terminate;
+};
+
+/**
+ * CPA component state
+ */
+struct cpa_component {
+	/** component base class */
+	struct rayo_component base;
+	/** true if ready to forward detector events */
+	int ready;
+	/** signals this component wants */
+	switch_hash_t *signals;
+};
+
+#define CPA_COMPONENT(x) ((struct cpa_component *)x)
+
+typedef void (* subscriber_execute_fn)(const char *jid, void *user_data);
+
+/**
+ * Request signals
+ */
+static void subscribe(const char *uuid, const char *signal_type, const char *jid)
+{
+	char *key = switch_mprintf("%s:%s", uuid, signal_type);
+	switch_mutex_lock(globals.subscribers_mutex);
+	{
+		switch_hash_t *signal_subscribers = switch_core_hash_find(globals.subscribers, key);
+		switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Subscribe %s => %s\n", signal_type, jid);
+		if (!signal_subscribers) {
+			switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Create %s subscriber hash\n", signal_type);
+			switch_core_hash_init(&signal_subscribers, NULL);
+			switch_core_hash_insert(globals.subscribers, key, signal_subscribers);
+		}
+		switch_core_hash_insert(signal_subscribers, jid, "1");
+	}
+	switch_mutex_unlock(globals.subscribers_mutex);
+	switch_safe_free(key);
+}
+
+/**
+ * Stop receiving signals
+ */
+static void unsubscribe(const char *uuid, const char *signal_type, const char *jid)
+{
+	char *key = switch_mprintf("%s:%s", uuid, signal_type);
+	switch_mutex_lock(globals.subscribers_mutex);
+	{
+		switch_hash_t *signal_subscribers = switch_core_hash_find(globals.subscribers, key);
+		if (signal_subscribers) {
+			switch_core_hash_delete(signal_subscribers, jid);
+			switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Unsubscribe %s => %s\n", signal_type, jid);
+
+			/* clean up hash if empty */
+			if (!switch_core_hash_first(signal_subscribers)) {
+				switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Destroy %s subscriber hash\n", signal_type);
+				switch_core_hash_destroy(&signal_subscribers);
+				switch_core_hash_delete(globals.subscribers, key);
+			}
+		}
+	}
+	switch_mutex_unlock(globals.subscribers_mutex);
+	switch_safe_free(key);
+}
+
+/**
+ * Execute function for each subscriber
+ */
+static void subscriber_execute(const char *uuid, const char *signal_type, subscriber_execute_fn callback, void *user_data)
+{
+	switch_event_t *subscriber_list = NULL;
+	switch_event_header_t *subscriber = NULL;
+
+	/* fetch list of subscribers */
+	char *key = switch_mprintf("%s:%s", uuid, signal_type);
+	switch_event_create_subclass(&subscriber_list, SWITCH_EVENT_CLONE, NULL);
+	switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Subscriber execute %s\n", signal_type);
+	switch_mutex_lock(globals.subscribers_mutex);
+	{
+		switch_hash_index_t *hi = NULL;
+		switch_hash_t *signal_subscribers = switch_core_hash_find(globals.subscribers, key);
+		if (signal_subscribers) {
+			for (hi = switch_core_hash_first(signal_subscribers); hi; hi = switch_core_hash_next(hi)) {
+				const void *jid;
+				void *dont_care;
+				switch_core_hash_this(hi, &jid, NULL, &dont_care);
+				switch_event_add_header_string(subscriber_list, SWITCH_STACK_BOTTOM, "execute", (const char *)jid);
+			}
+		} else {
+			switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "No subscribers for %s\n", signal_type);
+		}
+	}
+	switch_mutex_unlock(globals.subscribers_mutex);
+	switch_safe_free(key);
+
+	/* execute function for each subscriber */
+	for (subscriber = subscriber_list->headers; subscriber; subscriber = subscriber->next) {
+		callback(subscriber->value, user_data);
+	}
+
+	switch_event_destroy(&subscriber_list);
+}
+
+/**
+ * Stop all CPA detectors
+ */
+static void stop_cpa_detectors(struct cpa_component *cpa)
+{
+	if (cpa->signals) {
+		switch_hash_index_t *hi = NULL;
+		for (hi = switch_core_hash_first(cpa->signals); hi; hi = switch_core_hash_next(hi)) {
+			const char *signal_type;
+			struct cpa_signal *cpa_signal = NULL;
+			switch_core_hash_this(hi, (const void **)&signal_type, NULL, (void **)&cpa_signal);
+			if (cpa_signal) {
+				rayo_cpa_detector_stop(RAYO_COMPONENT(cpa)->parent->id, cpa_signal->name);
+				unsubscribe(RAYO_COMPONENT(cpa)->parent->id, cpa_signal->name, RAYO_JID(cpa));
+			}
+		}
+		switch_core_hash_destroy(&cpa->signals);
+		cpa->signals = NULL;
+	}
+	unsubscribe(RAYO_COMPONENT(cpa)->parent->id, "hangup", RAYO_JID(cpa));
+}
+
+/**
+ * Stop execution of CPA component
+ */
+static iks *stop_cpa_component(struct rayo_actor *component, struct rayo_message *msg, void *data)
+{
+	stop_cpa_detectors(CPA_COMPONENT(component));
+	rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP);
+	return iks_new_iq_result(msg->payload);
+}
+
+/**
+ * Forward CPA signal to client
+ */
+static void rayo_cpa_detector_event(const char *jid, void *user_data)
+{
+	struct rayo_actor *component = RAYO_LOCATE(jid);
+	if (component) {
+		if (CPA_COMPONENT(component)->ready) {
+			switch_event_t *event = (switch_event_t *)user_data;
+			const char *signal_type = switch_event_get_header(event, "signal-type");
+			struct cpa_signal *cpa_signal = switch_core_hash_find(CPA_COMPONENT(component)->signals, signal_type);
+			switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_COMPONENT(component)->parent->id), SWITCH_LOG_DEBUG, "Handling CPA event\n");
+			if (cpa_signal) {
+				const char *value = switch_event_get_header(event, "value");
+				const char *duration = switch_event_get_header(event, "duration");
+				if (cpa_signal->terminate) {
+					iks *complete_event;
+					iks *signal_xml;
+
+					stop_cpa_detectors(CPA_COMPONENT(component));
+
+					/* send complete event to client */
+					complete_event = rayo_component_create_complete_event(RAYO_COMPONENT(component), "signal", RAYO_CPA_NS);
+					signal_xml = iks_find(complete_event, "complete");
+					signal_xml = iks_find(signal_xml, "signal");
+					iks_insert_attrib(signal_xml, "type", signal_type);
+					if (!zstr(value)) {
+						iks_insert_attrib(signal_xml, "value", value);
+					}
+					if (!zstr(duration)) {
+						iks_insert_attrib(signal_xml, "duration", duration);
+					}
+					rayo_component_send_complete_event(RAYO_COMPONENT(component), complete_event);
+				} else {
+					/* send event to client */
+					iks *signal_event = iks_new_presence("signal", RAYO_CPA_NS, RAYO_JID(component), RAYO_COMPONENT(component)->client_jid);
+					iks *signal_xml = iks_find(signal_event, "signal");
+					iks_insert_attrib(signal_xml, "type", signal_type);
+					if (!zstr(value)) {
+						iks_insert_attrib(signal_xml, "value", value);
+					}
+					if (!zstr(duration)) {
+						iks_insert_attrib(signal_xml, "duration", duration);
+					}
+					RAYO_SEND_REPLY(component, RAYO_COMPONENT(component)->client_jid, signal_event);
+				}
+			}
+		} else {
+			switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_COMPONENT(component)->parent->id), SWITCH_LOG_DEBUG, "Skipping CPA event\n");
+		}
+		RAYO_UNLOCK(component);
+	}
+}
+
+/**
+ * Handle CPA signal-type event
+ */
+static void on_rayo_cpa_detector_event(switch_event_t *event)
+{
+	subscriber_execute(switch_event_get_header(event, "Unique-ID"), switch_event_get_header(event, "signal-type"), rayo_cpa_detector_event, event);
+}
+
+/**
+ * Handle CPA completion because of hangup
+ */
+static void rayo_cpa_component_hangup(const char *jid, void *user_data)
+{
+	struct rayo_actor *component = RAYO_LOCATE(jid);
+	if (component) {
+		stop_cpa_detectors(CPA_COMPONENT(component));
+		rayo_component_send_complete(RAYO_COMPONENT(component), COMPONENT_COMPLETE_HANGUP);
+		RAYO_UNLOCK(component);
+	}
+}
+
+/**
+ * Handle hungup call event 
+ */
+static void on_channel_hangup_complete_event(switch_event_t *event)
+{
+	subscriber_execute(switch_event_get_header(event, "Unique-ID"), "hangup", rayo_cpa_component_hangup, event);
+}
+
+/**
+ * Start CPA
+ */
+iks *rayo_cpa_component_start(struct rayo_actor *call, struct rayo_message *msg, void *session_data)
+{
+	iks *iq = msg->payload;
+	switch_core_session_t *session = (switch_core_session_t *)session_data;
+	iks *input = iks_find(iq, "input");
+	switch_memory_pool_t *pool = NULL;
+	struct cpa_component *component = NULL;
+	int have_grammar = 0;
+	iks *grammar = NULL;
+
+	/* create CPA component */
+	switch_core_new_memory_pool(&pool);
+	component = switch_core_alloc(pool, sizeof(*component));
+	rayo_component_init((struct rayo_component *)component, pool, RAT_CALL_COMPONENT, "cpa", NULL, call, iks_find_attrib(iq, "from"));
+
+	switch_core_hash_init(&component->signals, pool);
+
+	/* start CPA detectors */
+	for (grammar = iks_find(input, "grammar"); grammar; grammar = iks_next_tag(grammar)) {
+		if (!strcmp("grammar", iks_name(grammar))) {
+			const char *error_str = "";
+			const char *url = iks_find_attrib_soft(grammar, "url");
+			char *url_dup;
+			char *url_params;
+
+			if (zstr(url)) {
+				stop_cpa_detectors(component);
+				RAYO_UNLOCK(component);
+				RAYO_DESTROY(component);
+				return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Missing grammar URL");
+			}
+			have_grammar = 1;
+
+			url_dup = strdup(url);
+			if ((url_params = strchr(url_dup, '?'))) {
+				*url_params = '\0';
+				url_params++;
+			}
+
+			if (switch_core_hash_find(component->signals, url)) {
+				free(url_dup);
+				stop_cpa_detectors(component);
+				RAYO_UNLOCK(component);
+				RAYO_DESTROY(component);
+				return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Duplicate URL");
+			}
+
+			/* start detector */
+			/* TODO return better reasons... */
+			if (rayo_cpa_detector_start(switch_core_session_get_uuid(session), url_dup, &error_str)) {
+				struct cpa_signal *cpa_signal = switch_core_alloc(pool, sizeof(*cpa_signal));
+				cpa_signal->terminate = !zstr(url_params) && strstr(url_params, "terminate=true");
+				cpa_signal->name = switch_core_strdup(pool, url_dup);
+				switch_core_hash_insert(component->signals, cpa_signal->name, cpa_signal);
+				subscribe(switch_core_session_get_uuid(session), cpa_signal->name, RAYO_JID(component));
+			} else {
+				free(url_dup);
+				stop_cpa_detectors(component);
+				RAYO_UNLOCK(component);
+				RAYO_DESTROY(component);
+				return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, error_str);
+			}
+
+			free(url_dup);
+		}
+	}
+
+	if (!have_grammar) {
+		stop_cpa_detectors(component);
+		RAYO_UNLOCK(component);
+		RAYO_DESTROY(component);
+		return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "No grammar defined");
+	}
+
+	/* acknowledge command */
+	rayo_component_send_start(RAYO_COMPONENT(component), iq);
+
+	/* TODO hangup race condition */	
+	subscribe(switch_core_session_get_uuid(session), "hangup", RAYO_JID(component));
+
+	/* ready to forward detector events */
+	component->ready = 1;
+
+	return NULL;
+}
+
+/**
+ * Load input CPA
+ * @param module_interface
+ * @param pool memory pool
+ * @param config_file
+ * @return SWITCH_STATUS_SUCCESS if successfully loaded
+ */
+switch_status_t rayo_cpa_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
+{
+	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "cpa", "set:"RAYO_EXT_NS":stop", stop_cpa_component);
+	switch_event_bind("rayo_cpa_component", SWITCH_EVENT_CUSTOM, "rayo::cpa", on_rayo_cpa_detector_event, NULL);
+	switch_event_bind("rayo_cpa_component", SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE, NULL, on_channel_hangup_complete_event, NULL);
+	
+	globals.pool = pool;
+	switch_core_hash_init(&globals.subscribers, pool);
+	switch_mutex_init(&globals.subscribers_mutex, SWITCH_MUTEX_NESTED, pool);
+
+	return rayo_cpa_detector_load(module_interface, pool, config_file);
+}
+
+/**
+ * Stop input CPA
+ */
+void rayo_cpa_component_shutdown(void)
+{
+	switch_event_unbind_callback(on_rayo_cpa_detector_event);
+	switch_event_unbind_callback(on_channel_hangup_complete_event);
+	rayo_cpa_detector_shutdown();
+	switch_core_hash_destroy(&globals.subscribers);
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
+ */
diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_component.h b/src/mod/event_handlers/mod_rayo/rayo_cpa_component.h
new file mode 100644
index 0000000000..36354c9508
--- /dev/null
+++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_component.h
@@ -0,0 +1,53 @@
+/*
+ * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2014, Grasshopper
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is Grasshopper
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris.rienzo@grasshopper.com>
+ *
+ * rayo_cpa_component.h -- Rayo call progress analysis component
+ *
+ */
+#ifndef RAYO_CPA_COMPONENT_H
+#define RAYO_CPA_COMPONENT_H
+
+#include <switch.h>
+#include <iksemel.h>
+
+#include "mod_rayo.h"
+
+extern switch_status_t rayo_cpa_component_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
+extern void rayo_cpa_component_shutdown(void);
+extern iks *rayo_cpa_component_start(struct rayo_actor *call, struct rayo_message *msg, void *session_data);
+
+#endif
+
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
+ */
diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c
index 9267e05456..27ce9a13be 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.c
@@ -155,21 +155,27 @@ static void rayo_cpa_detector_event(switch_event_t *event)
 		}
 		if (!zstr(signal_type)) {
 			switch_event_t *cpa_event;
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got Rayo CPA event %s\n", signal_type);
+			const char *uuid = switch_event_get_header(event, "Unique-ID");
+			if (zstr(uuid)) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s %s event is missing call UUID!\n", detector->name, signal_type);
+				return;
+			}
+			switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Got Rayo CPA event %s\n", signal_type);
 			if (switch_event_create_subclass(&cpa_event, SWITCH_EVENT_CUSTOM, "rayo::cpa") == SWITCH_STATUS_SUCCESS) {
-				switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "detector-name", detector->name);
-				switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "detector-uuid", detector->uuid);
-				switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "signal-type", signal_type);
+				switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "Unique-ID", uuid);
+				switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "detector-name", detector->name);
+				switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "detector-uuid", detector->uuid);
+				switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "signal-type", "%s%s:%s", RAYO_CPA_BASE, signal_type, RAYO_VERSION);
 				if (!zstr(detector->signal_value_header)) {
 					const char *value = switch_event_get_header(event, detector->signal_value_header);
 					if (!zstr(value)) {
-						switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "value", value);
+						switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "value", value);
 					}
 				}
 				if (!zstr(detector->signal_duration_header)) {
 					const char *duration = switch_event_get_header(event, detector->signal_duration_header);
 					if (!zstr(duration)) {
-						switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "duration", duration);
+						switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "duration", duration);
 					}
 				}
 				switch_event_fire(&cpa_event);
diff --git a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h
index 335d1c3a1b..fcdc35f2f4 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h
+++ b/src/mod/event_handlers/mod_rayo/rayo_cpa_detector.h
@@ -33,21 +33,6 @@
 
 #include "mod_rayo.h"
 
-#define RAYO_CPA_BASE RAYO_BASE "cpa:"
-#define RAYO_CPA_NS RAYO_CPA_BASE RAYO_VERSION
-
-#define RAYO_CPA_BEEP_NS RAYO_CPA_BASE "beep:" RAYO_VERSION
-#define RAYO_CPA_DTMF_NS RAYO_CPA_BASE "dtmf:" RAYO_VERSION
-#define RAYO_CPA_SPEECH_NS RAYO_CPA_BASE "speech:" RAYO_VERSION
-#define RAYO_CPA_FAX_CED_NS RAYO_CPA_BASE "fax-ced:" RAYO_VERSION
-#define RAYO_CPA_FAX_CNG_NS RAYO_CPA_BASE "fax-cng:" RAYO_VERSION
-#define RAYO_CPA_RING_NS RAYO_CPA_BASE "ring:" RAYO_VERSION
-#define RAYO_CPA_BUSY_NS RAYO_CPA_BASE "busy:" RAYO_VERSION
-#define RAYO_CPA_CONGESTION_NS RAYO_CPA_BASE "congestion:" RAYO_VERSION
-#define RAYO_CPA_SIT_NS RAYO_CPA_BASE "sit:" RAYO_VERSION
-#define RAYO_CPA_MODEM_NS RAYO_CPA_BASE "modem:" RAYO_VERSION
-#define RAYO_CPA_OFFHOOK_NS RAYO_CPA_BASE "offhook:" RAYO_VERSION
-
 extern switch_status_t rayo_cpa_detector_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file);
 extern void rayo_cpa_detector_shutdown(void);
 extern int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const char **error_detail);
diff --git a/src/mod/event_handlers/mod_rayo/rayo_elements.c b/src/mod/event_handlers/mod_rayo/rayo_elements.c
index 78209c3bd6..1720471e31 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_elements.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_elements.c
@@ -33,7 +33,7 @@
  */
 ELEMENT(RAYO_INPUT)
 	ATTRIB(xmlns,, any)
-	STRING_ATTRIB(mode, any, "any,dtmf,voice")
+	STRING_ATTRIB(mode, any, "any,dtmf,voice,cpa")
 	OPTIONAL_ATTRIB(terminator,, dtmf_digit)
 	ATTRIB(recognizer,, any)
 	ATTRIB(language, en-US, any)
diff --git a/src/mod/event_handlers/mod_rayo/rayo_input_component.c b/src/mod/event_handlers/mod_rayo/rayo_input_component.c
index 4ea0e5f600..41383a1d23 100644
--- a/src/mod/event_handlers/mod_rayo/rayo_input_component.c
+++ b/src/mod/event_handlers/mod_rayo/rayo_input_component.c
@@ -27,7 +27,7 @@
  *
  */
 #include "rayo_components.h"
-#include "rayo_cpa_detector.h"
+#include "rayo_cpa_component.h"
 #include "rayo_elements.h"
 #include "srgs.h"
 #include "nlsml.h"
@@ -360,10 +360,14 @@ static iks *start_call_input(struct input_component *component, switch_core_sess
 	component->speech_mode = strcmp(iks_find_attrib_soft(input, "mode"), "dtmf");
 	if (component->speech_mode && handler->voice_component) {
 		/* don't allow multi voice input */
+		RAYO_UNLOCK(component);
+		RAYO_DESTROY(component);
 		return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple voice input is not allowed");
 	}
 	if (!component->speech_mode && handler->dtmf_component) {
 		/* don't allow multi dtmf input */
+		RAYO_UNLOCK(component);
+		RAYO_DESTROY(component);
 		return iks_new_error_detailed(iq, STANZA_ERROR_CONFLICT, "Multiple dtmf input is not allowed");
 	}
 
@@ -538,17 +542,20 @@ static iks *start_call_input_component(struct rayo_actor *call, struct rayo_mess
 	struct input_component *input_component = NULL;
 	const char *error = NULL;
 
+	/* Start CPA */
+	if (!strcmp(iks_find_attrib_soft(input, "mode"), "cpa")) {
+		return rayo_cpa_component_start(call, msg, session_data);
+	}
+
+	/* start input */
 	if (!validate_call_input(input, &error)) {
 		return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, error);
 	}
 
-	/* create component */
 	switch_core_new_memory_pool(&pool);
 	input_component = switch_core_alloc(pool, sizeof(*input_component));
 	rayo_component_init(RAYO_COMPONENT(input_component), pool, RAT_CALL_COMPONENT, "input", component_id, call, iks_find_attrib(iq, "from"));
-
-	/* start input */
-	return start_call_input(input_component, session, iks_find(iq, "input"), iq, NULL, 0);
+	return start_call_input(input_component, session, input, iq, NULL, 0);
 }
 
 /**
@@ -761,7 +768,7 @@ switch_status_t rayo_input_component_load(switch_loadable_module_interface_t **m
 	rayo_actor_command_handler_add(RAT_CALL_COMPONENT, "input", "set:"RAYO_INPUT_NS":start-timers", start_timers_call_input_component);
 	switch_event_bind("rayo_input_component", SWITCH_EVENT_DETECTED_SPEECH, SWITCH_EVENT_SUBCLASS_ANY, on_detected_speech_event, NULL);
 
-	return rayo_cpa_detector_load(module_interface, pool, config_file);
+	return rayo_cpa_component_load(module_interface, pool, config_file);
 }
 
 /**
@@ -773,7 +780,7 @@ switch_status_t rayo_input_component_shutdown(void)
 	srgs_parser_destroy(globals.parser);
 	switch_event_unbind_callback(on_detected_speech_event);
 
-	rayo_cpa_detector_shutdown();
+	rayo_cpa_component_shutdown();
 
 	return SWITCH_STATUS_SUCCESS;
 }