diff --git a/js/app/views/EpnTapUI.js b/js/app/views/EpnTapUI.js
index d75b490..3021a93 100644
--- a/js/app/views/EpnTapUI.js
+++ b/js/app/views/EpnTapUI.js
@@ -101,7 +101,9 @@ Ext.create('Ext.data.Store', {
 		{name: 'reference_url', type: 'string'},
 		{name: 'created', type: 'date', dateFormat: 'c'},
 		{name: 'updated', type: 'date', dateFormat: 'c'},
-		{name: 'nb_results', type: 'integer'}
+		{name: 'nb_results', type: 'integer'},
+		{name: 'info', type: 'string'},
+		{name: 'error', type: 'string'}
 	],
 	proxy: {
 		type: 'ajax',
@@ -476,7 +478,7 @@ Ext.define('amdaUI.EpnTapUI', {
 	*/
 	createServicesGrid: function() {
 		var nbResRender = function(val) {
-			if(val<0 || isNaN(val)) {
+			if(val < 0) {
 				return '-';
 			} else if(val >= 1000*1000) {
 				return (val/(1000*1000)).toPrecision(3) + 'm';
@@ -499,7 +501,9 @@ Ext.define('amdaUI.EpnTapUI', {
 			viewConfig: {
 				getRowClass: function(record, index) {
 					var nb_res = record.get('nb_results');
-					if (nb_res <= 0 || isNaN(nb_res)) {
+					if(record.get('error').length > 0) {
+						return 'error_row';
+					} else if (nb_res <= 0) {
 						return 'disabled_row';
 					}
 				}
@@ -526,6 +530,11 @@ Ext.define('amdaUI.EpnTapUI', {
 							'updated': 'Updated on'};
 					var service = epnTapServicesGrid.getView().getRecord(tooltip.triggerElement);
 					var ttContent = '<h3>' + service.get('short_name') + '</h3><ul>';
+					if(service.get('error').length > 0) {
+						ttContent += '<p style="color:IndianRed">' + service.get('error') + '</p><br/>';
+					} else if(service.get('info').length > 0) {
+						ttContent += '<p style="color:green">' + service.get('info') + '</p><br/>';
+					}
 					for (var column in column_titles) {
 						var col_content = service.get(column);
 						if(column === 'content_level' && col_content!=='') {
diff --git a/js/resources/css/amda.css b/js/resources/css/amda.css
index 61cfc5e..5c15bb3 100644
--- a/js/resources/css/amda.css
+++ b/js/resources/css/amda.css
@@ -489,6 +489,9 @@ p + p {
 }
 
 .disabled_row {
-	pointer-events: none;
-    color: gray;
+	color: gray;
+}
+
+.error_row {
+	color: IndianRed;
 }
diff --git a/php/epntap.php b/php/epntap.php
index e9bd0a5..69ab34a 100644
--- a/php/epntap.php
+++ b/php/epntap.php
@@ -16,9 +16,11 @@ switch ($action) {
 	case 'getNbResults':
 		echo getNbResults();
 		break;
-
-	default:
+	case 'getGranules':
+		echo json_encode(getGranules());
 		break;
+	default:
+		echo 'unknown action';
 }
 
 function resolver() {
@@ -50,6 +52,7 @@ function getServices() {
 			for($j=0; $j<count($services); $j++) {
 				$services[$j]['id'] = generateServiceId($services[$j]);
 				$services[$j]['nb_results'] = -1;
+				$services[$j]['info'] = 'Please make a query first.';
 				if($services[$j]['id'] == 'cdpp/amda/amdadb') {
 					array_splice($services, $j, 1);
 					$j-=1;
@@ -88,6 +91,21 @@ function getNbResults() {
 	}
 }
 
+function getGranules() {
+	$url = filter_var($_GET['url'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
+	$tableName = filter_var($_GET['tableName'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
+	$targetName = filter_var($_GET['targetName'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
+	$productTypes = filter_var($_GET['productTypes'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
+	$timeMin = filter_var($_GET['timeMin'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
+	$timeMax = filter_var($_GET['timeMax'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
+
+	// TODO: make this more beatiful
+	// TODO find a way to handle 'non existing key errors' (and then add access_format)
+	$query = "SELECT dataproduct_type, target_name, time_min, time_max, granule_uid, access_estsize, access_url, thumbnail_url FROM $tableName" . createFilter($targetName, $productTypes, $timeMin, $timeMax);
+	$result = request($url, $query);
+	return $result;
+}
+
 // ----- utils -----
 
 function request($access_url, $query) {
--
libgit2 0.21.2