diff --git a/public_html/index.html b/public_html/index.html
index 0cc50c6f81ea8fec6922795ecb1a4c41edc3e084..d6626527b5e4c0bb518e4d7d0e303ca1cfa98d92 100644
--- a/public_html/index.html
+++ b/public_html/index.html
@@ -10,11 +10,16 @@
 
 <body>
     <header>
-        <h1>📋 Dashboard</h1>
+        <h1>📋 Appmonitor dashboard</h1>
     </header>
 
-    <section id="app-section"><h2>📦 fetching...</h2></section>
-    <section id="msg-section"><h2>🛎️ fetching...</h2></section>
+    <section id="tag-section"><h2>🕛 fetching...</h2></section>
+    <section id="app-section"><h2>🕛 fetching...</h2></section>
+
+    <!--
+    <section id="msg-section"><h2>🛎️ Messages (coming soon)</h2></section>
+    -->
+    <br><br><br><br>
 
     <footer>
         POC :: &copy; IML 2022
diff --git a/public_html/javascript/functions.js b/public_html/javascript/functions.js
index c98b4e21f1c03bbff6323a3035246128aeb0ad35..cb4db7975a22cea2edb75dfaa990d7cc6c1e823e 100644
--- a/public_html/javascript/functions.js
+++ b/public_html/javascript/functions.js
@@ -15,28 +15,162 @@ const AM_RESULTS={
     3: 'error'
 };
 
-
 const OUT_ID_APPS='app-section';
+const OUT_ID_TAGS='tag-section';
+const ID_TAGINPUT='E_TAGS';
 
 // ----------------------------------------------------------------------
 // VARS
 // ----------------------------------------------------------------------
 
-
+var AM_TAGURL=false;
 
 // ----------------------------------------------------------------------
 // FUNCTIONS
 // ----------------------------------------------------------------------
 
+/**
+ * get url to fetch the tags
+ * @returns string
+ */
+function _getUrlForTags(){
+    return AM_SERVER_URL+'/v1/apps/tags';
+}
+
+/**
+ * get url to fetch all applications with given tags
+ * @param {string} tags list of tags - separated by comma
+ * @returns 
+ */
+function _getUrlWithTags(tags){
+    return AM_SERVER_URL+'/v1/apps/tags/'+tags+'/all';
+}
+
+/**
+ * helper: get a table row with data of an application
+ * @param {string} sLabel 
+ * @param {string} sText 
+ * @returns string
+ */
 function _appItem(sLabel,sText){
     return '<tr><td>'+sLabel+'&nbsp;&nbsp;</td><td>'+sText+'</td></tr>'
 }
 
-function _getSingleAppStatus(sData){
+/**
+ * helper: get a 2 digit count
+ * @param {int} i 
+ * @returns 
+ */
+function _2digits(i){
+    return i<10 ? "0"+i : i;
+}
+
+// ----------------------------------------------------------------------
+// FUNCTIONS :: TAGS
+// ----------------------------------------------------------------------
+
+
+function tagAdd(tagname){
+    var o=document.getElementById(ID_TAGINPUT);
+    o.value+=(o.value ? "," : "") +tagname;
+    getAppstatus();
+}
+
+function tagClear(){
+    var o=document.getElementById(ID_TAGINPUT);
+    o.value="";
+    getAppstatus();
+}
+
+/**
+ * called from getTags
+ * @param {string} sData JSOM Response
+ * @returns 
+ */
+function _getTaglist(sData){
+    var sReturn='';
+    var sTags='';
     let aData=JSON.parse(sData);
 
+    sReturn+='<input id="'+ID_TAGINPUT+'" type="text" size="50" value="'+AM_TAGS+'"'
+            +' onchange="getAppstatus()"'
+            +' onkeypress="getAppstatus()"'
+            +' onkeydown="getAppstatus()"'
+            +' onkeyup="getAppstatus()"'
+        +'>'
+        +'<button onclick="tagClear();return false;"> ❌ </button><br><br>'
+        ;
+
+    for (var s in aData['tags']){
+        sTags+=(sTags ? ",": "") + aData['tags'][s];
+        sReturn+='<button onclick="tagAdd(\''+aData['tags'][s]+'\'); return false;">'+aData['tags'][s]+'</button>'
+    }
+    return sReturn;
+}
+
+
+
+/**
+ * fetch appmonitor api - taglist
+ * called from getAppstatus
+ */
+ async function getTags(){
+    let out = '<h2>🏷️ Tags</h2>';
+    AM_TAGURL=_getUrlForTags();
+
+    let response = await fetch(AM_TAGURL);
+    if (response.ok) {
+        out+=_getTaglist(await response.text());
+    } else {
+        out+='<div class="app result1">'
+            +'ERROR '+response.status+': '+response.statusText + ' - '
+            +AM_TAGURL
+            +'</div>';
+    } 
+    
+    o=document.getElementById(OUT_ID_TAGS);
+    o.innerHTML=out;
+}
+
+
+// ----------------------------------------------------------------------
+// FUNCTIONS :: APPS
+// ----------------------------------------------------------------------
+
+/**
+ * loop over apps and render results as html
+ * @param {string}  sData  response body from api request (JSON string)
+ * @returns string
+ */
+function _getAllAppsStatus(sData){
+    var sReturn="";
+    var oDate=new Date;
+
+    sReturn+=''
+            +'<h3>Tags: ' + tags + '</h3>'
+            + '<p>' 
+            +_2digits(oDate.getHours())
+            +':'+_2digits(oDate.getMinutes())
+            +':'+_2digits(oDate.getSeconds())
+            +' (update every '+REFRESHTIME+' sec)'
+        +'</p>';
+    let aAllData=JSON.parse(sData);
+    for (var key in aAllData){
+        sReturn+=_getSingleAppStatus(aAllData[key]);
+    }
+    return sReturn;
+}
+
+/**
+ * render application status of a single application as html
+ * @param {array} aData monitoring data from appmonitor api
+ * @returns string
+ */
+function _getSingleAppStatus(aData){
+    // let aData=JSON.parse(sData);
+
     // DEBUG
-    console.log(aData);
+    // console.log(aData);
 
     // ------ checks
     let sChecks='';
@@ -55,6 +189,7 @@ function _getSingleAppStatus(sData){
     let sReturn='<div class="app result'+ aData.meta.result +'">'
         +'<div class="title" onclick="$(this).next().toggle()">'
             +'<span class="float-right">'+AM_RESULTS[aData.meta.result]+'</span>'
+            +'<span class="float-right url">'+aData.result.url+'</span>'
             +aData.meta.website
         +'</div>'
         ;
@@ -79,23 +214,35 @@ function _getSingleAppStatus(sData){
     return sReturn;
 }
 
+/**
+ * fetch appmonitor api - status of apps
+ */
 async function getAppstatus(){
-    let out = '<h2>📦 Applications</h2>';
+    if(!AM_TAGURL){
+        getTags();
+    }
 
-    for (var i=0; i<AM_URLS.length; i++){
+    var o=document.getElementById(ID_TAGINPUT);
+    tags=o ? o.value : AM_TAGS;
 
-    
-        let response = await fetch(AM_SERVER_URL + AM_URLS[i]);
+    let out = '<h2>📦 Applications</h2>';
+    // for (var i=0; i<AM_TAGS.length; i++){
+
+        // let apiurl=_getUrlWithTags(AM_TAGS[i])
+        let apiurl=_getUrlWithTags(tags)
+        let response = await fetch(apiurl);
         if (response.ok) {
-            out+=_getSingleAppStatus(await response.text());
+            out+=_getAllAppsStatus(await response.text());
         } else {
-            out+='<div class="app result1">ERROR: '
-                +AM_SERVER_URL + AM_URLS[i]
+            out+='<div class="app result1">'
+                +'ERROR '+response.status+': '+response.statusText + ' - '
+                +apiurl
                 +'</div>';
         } 
-    }
+    // }
     o=document.getElementById(OUT_ID_APPS);
     o.innerHTML=out;
+    window.setTimeout("getAppstatus()", REFRESHTIME*1000);
 }
 
 
@@ -104,7 +251,7 @@ async function getAppstatus(){
 // ----------------------------------------------------------------------
 
 
-window.setTimeout("getAppstatus()", 300);
+window.setTimeout("getAppstatus()", 500);
 
 
 // ----------------------------------------------------------------------
diff --git a/public_html/javascript/inc_config.js.dist b/public_html/javascript/inc_config.js.dist
new file mode 100644
index 0000000000000000000000000000000000000000..1b65e7d4a95bf4976ad26c6f3c9df06f1ba8616d
--- /dev/null
+++ b/public_html/javascript/inc_config.js.dist
@@ -0,0 +1,3 @@
+const AM_SERVER_URL='https://appmonitor.example.com/api';
+const AM_TAGS='live,myapp';
+const REFRESHTIME=30; // in sec
diff --git a/public_html/main.css b/public_html/main.css
index 4dfde07899ef745205e8add989d57d9949049ab6..6f5184206174647c6f4763286d30e3f8c708fe67 100644
--- a/public_html/main.css
+++ b/public_html/main.css
@@ -3,7 +3,7 @@
 :root{
     --color-0: #345;
     --color-h1: #c54;
-    --color-h2: #567;
+    --color-h2: #569;
     --color-links: #8ae;
 
     --color-result-0: 
@@ -13,27 +13,33 @@
 
 a{color: var(--color-links);}
 body{
+    font-size: 1em;
     margin: 1em;
     color: var(--color-0);
     background: var(--bg-0);
     font-family: verdana,arial;
 }
 
+button{ border:1px solid #ccc; background: linear-gradient(#f8f8f8, #eee); border-radius: 0.3em; margin: 0 0.5em 0.5em 0; padding: 0.5em;}
+button:hover{ background: linear-gradient(#eee, #ddd); border-radius: 0.3em; margin: 0 0.5em 0.5em 0; padding: 0.5em;}
+button:active{ border:1px solid #fc2;}
+input{ border:1px solid #ccc; padding: 0.4em;}
+
 footer{ position: fixed; bottom: 0; left: 0; width: 100%; padding: 1em; background: #eee; text-align: center;}
 h1{color: var(--color-h1)}
-h2{color: var(--color-h2)}
+h2{color: var(--color-h2); margin-left: -1em;}
 
 section{
-    margin: 1em 0;
-    padding: 0.2em;
-    border-top: 2px solid #eee;
+    margin: 2em 0;
+    padding: 0.2em 2em;
+    border-top: 0px solid #abc;
     border-radius: 1em;
 }
 
 td{vertical-align: top;}
 
 .app{
-    margin: 1em;
+    margin: 1em 0;
     border: 2px solid;
     border-radius: 0.3em;
     padding: 1em; 
@@ -43,6 +49,7 @@ td{vertical-align: top;}
 
 .app .title{font-weight: bold; font-size: 130%; cursor: pointer;}
 
+.app .url{font-size: 80%; font-weight: normal; margin-right: 2em;}
 .app .details{display: none;}
 
 .result0{background:#dfd    !important; background: linear-gradient(#dfd,#beb)!important;       color:#080}