diff --git a/appserver.export.inc b/appserver.export.inc
index a7a2463..d1fd6b3 100644
--- a/appserver.export.inc
+++ b/appserver.export.inc
@@ -51,7 +51,10 @@ function appserver_create_manifest($tid){
     	$manifest['apps'][$cur]->screenshots[] = url(file_create_url($screenshot['uri']),array("absolute" => TRUE)) ;            
     }
     $manifest['apps'][$cur]->logo = url(file_create_url($logo['uri']),array("absolute" => TRUE)) ;            
-  
+    
+    //add in the voting results
+    $manifest['apps'][$cur]->rating = appserver_voting_get_votes_for_release($node->nid);
+
     $cur++;
   }
   
diff --git a/appserver.info b/appserver.info
index 8feefd0..8cc2727 100644
--- a/appserver.info
+++ b/appserver.info
@@ -5,6 +5,7 @@ dependencies[] = "list"
 dependencies[] = "node_reference"
 dependencies[] = "strongarm"
 dependencies[] = "taxonomy"
+dependencies[] = "votingapi"
 features[ctools][] = "strongarm:strongarm:1"
 features[field][] = "node-app-body"
 features[field][] = "node-app-field_app_releases"
diff --git a/appserver.module b/appserver.module
index 21b315a..2f43765 100644
--- a/appserver.module
+++ b/appserver.module
@@ -3,6 +3,7 @@
  * @file appserver.module
  */
 
+include_once('appserver.voting.inc');
 include_once('appserver.export.inc');
 include_once('appserver.features.inc');
 
@@ -34,7 +35,12 @@ function appserver_menu() {
     'access callback' => TRUE,
     'type' => MENU_CALLBACK
   );
-
+  //support voting
+  $items['apps/vote/%/%'] = array(
+    'page callback' => 'appserver_voting_vote',
+    'page arguments' => array(2, 3),
+    'access callback' => TRUE,
+  );
   return $items;
 }
 
diff --git a/appserver.voting.inc b/appserver.voting.inc
new file mode 100644
index 0000000..99f7062
--- /dev/null
+++ b/appserver.voting.inc
@@ -0,0 +1,146 @@
+<?php
+/**
+ *	Provides an interface for voting on apps
+ *	Requires votingapi
+ */
+
+/**
+ *	Record a vote using voting api
+ *	Callback from the menu
+ *	@param $machine_name - the machine name of the App
+ *	@param $vote - 0-5, the vote
+ * 	@return NULL - but outputs json response
+ */
+function appserver_voting_vote ($machine_name, $vote) {
+	//get the app by machine name
+	$query = new EntityFieldQuery();
+	$app = $query
+      ->entityCondition('entity_type', 'node')
+      ->entityCondition('bundle', 'app')
+      ->fieldCondition('field_machine_name', 'value', $machine_name, '=')
+      ->execute();
+
+  if(!empty($app['node'])) {
+  	//just grab the first one
+  	//$app = current($app);
+  	$app = current($app['node']);
+  	// make sure the vote is numeric, positive and 5 or less
+  	if (!is_numeric($vote) || $vote > 5 || $vote < 0) {
+  		appserver_voting_return_error('Invalid Vote', 'Vote was either not numeric or greater than 5');
+  	} else {
+
+	 		$vote = array(
+	 			'entity_type' => 'node',
+	 			'entity_id' => $app->nid,
+	 			'value' => $vote,
+		 	);
+		 	$votes = array($vote);
+
+		 	//create the criteria - we just want to key on the vote source and the app
+		 	$criteria = array(
+				'vote_source' => ip_address(),
+				'entity_id' => $app->nid,
+			);
+			//set the anon window to -1 so it will get ignored, then set it back
+			$anon_window = variable_get('votingapi_anonymous_window', 3600);
+
+			variable_set('votingapi_anonymous_window', -1);
+			$results = votingapi_set_votes($votes, $criteria);
+			variable_set('votingapi_anonymous_window', $anon_window);
+			
+			//format the results - first normalize to be like the return from votingapi_recalculate_results
+			$results = $results['node'][$app->nid];
+			$results = _appserver_voting_format_results($results);
+
+			appserver_voting_return_status('Vote Successfull', $results);
+		}
+	} else {
+		appserver_voting_return_error('Invalid App', 'No app found with that name');
+	}
+}
+
+/**
+ *	Return a json error if the voting failed
+ */
+function appserver_voting_return_error ($error, $message) {
+	drupal_json_output(array(
+			'status' => 'Error', 
+			'error' => $error, 
+			'message' => $message,
+	));
+}
+/**
+ *	Return a message as json
+ */
+function appserver_voting_return_status ($status, $message) {
+	drupal_json_output(array(
+		'status' => $status,
+		'message' => $message,
+	));
+}
+
+/**
+ *	Utility function to retrieve the current vote total for an app
+ *	@param nid - the nid of the app to get the data for
+ *	@return an array in the form
+ *	  array(
+ *		  'count' => 54,
+ *		  'average' => 4,
+ *    )
+ */
+function appserver_voting_get_votes($nid) {
+	$results = votingapi_recalculate_results('node', $nid);
+	$results = _appserver_voting_format_results($results);
+	return $results;
+}
+
+/**
+ *	Get the app node from a release and calculate the results
+ *	@return same as appserver_voting_get_votes
+ *	@see appserver_voting_get_votes
+ */
+function appserver_voting_get_votes_for_release($nid) {
+	
+	$query = new EntityFieldQuery();
+	$app = $query
+      ->entityCondition('entity_type', 'node')
+      ->entityCondition('bundle', 'app')
+      ->fieldCondition('field_app_releases', 'nid', $nid, '=')
+      ->execute();
+
+  if(!empty($app['node'])){
+  	$result = appserver_voting_get_votes(key($app['node']));
+  } else {
+  	//just generate empty results
+  	$result = _appserver_voting_format_results(array());
+  }
+
+  return $result;
+}
+
+/**
+ *	Utility function to reformat the results into an easier to digest form
+ */
+function _appserver_voting_format_results($results) {
+	$ret = array();
+	if(!empty($results)){
+		foreach($results as $r){
+			switch ($r['function']) {
+				case 'count' :
+					$ret['count'] = (int) $r['value']; //unsure if we need this cast, seems to me to be cleaner
+				break;
+				case 'average' :
+					$ret['average'] = $r['value'];
+				break;
+			}
+		}
+	}
+	//no votes found
+	if(empty($ret)){
+		$ret = array(
+			'count' => 0,
+			'average' => 0,
+		);
+	}
+	return $ret;
+}
