diff --git a/.env b/.env
index 5d4e2bc6d0fcb3e591d87ea5e92ce6bc696622e8..c1aabe03015b67420d0c27a50e6da8a301078d79 100644
--- a/.env
+++ b/.env
@@ -39,3 +39,8 @@ OAUTH2_REDIRECT_URL=https://wg-gen-web-demo.127-0-0-1.fr
 
 # set provider name to fake to disable auth, also the default
 OAUTH2_PROVIDER_NAME=fake
+
+# https://github.com/jamescun/wg-api integration, user and password (basic auth) are optional
+WG_STATS_API=https://wg.example.digital/wg-api
+WG_STATS_API_USER=
+WG_STATS_API_PASS=
\ No newline at end of file
diff --git a/api/v1/status/status.go b/api/v1/status/status.go
new file mode 100644
index 0000000000000000000000000000000000000000..7d3124cb2eb764116b9fd432b746ced830effbd8
--- /dev/null
+++ b/api/v1/status/status.go
@@ -0,0 +1,44 @@
+package status
+
+import (
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	log "github.com/sirupsen/logrus"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/core"
+)
+
+// ApplyRoutes applies router to gin Router
+func ApplyRoutes(r *gin.RouterGroup) {
+	g := r.Group("/status")
+	{
+		g.GET("/interface", readInterfaceStatus)
+		g.GET("/clients", readClientStatus)
+	}
+}
+
+func readInterfaceStatus(c *gin.Context) {
+	status, err := core.ReadInterfaceStatus()
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to read interface status")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, status)
+}
+
+func readClientStatus(c *gin.Context) {
+	status, err := core.ReadClientStatus()
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to read client status")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, status)
+}
diff --git a/api/v1/v1.go b/api/v1/v1.go
index c7d5df5a119f29a8ec02c2f7b709440655300bda..d44e515a5d67a49e1ddc9b4de1d7c96956a1fd0a 100644
--- a/api/v1/v1.go
+++ b/api/v1/v1.go
@@ -5,6 +5,7 @@ import (
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/auth"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/client"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/server"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/status"
 )
 
 // ApplyRoutes apply routes to gin router
@@ -14,9 +15,9 @@ func ApplyRoutes(r *gin.RouterGroup, private bool) {
 		if private {
 			client.ApplyRoutes(v1)
 			server.ApplyRoutes(v1)
+			status.ApplyRoutes(v1)
 		} else {
 			auth.ApplyRoutes(v1)
-
 		}
 	}
 }
diff --git a/core/status.go b/core/status.go
new file mode 100644
index 0000000000000000000000000000000000000000..e2fc54e224a766067e17a4ce3a3f005bebe2b173
--- /dev/null
+++ b/core/status.go
@@ -0,0 +1,168 @@
+package core
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"time"
+
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
+)
+
+// apiError implements a top-level JSON-RPC error.
+type apiError struct {
+	Code    int    `json:"code"`
+	Message string `json:"message"`
+
+	Data interface{} `json:"data,omitempty"`
+}
+
+type apiRequest struct {
+	Version string          `json:"jsonrpc"`
+	Method  string          `json:"method"`
+	Params  json.RawMessage `json:"params,omitempty"`
+}
+
+type apiResponse struct {
+	Version string          `json:"jsonrpc"`
+	Result  interface{}     `json:"result,omitempty"`
+	Error   *apiError       `json:"error,omitempty"`
+	ID      json.RawMessage `json:"id"`
+}
+
+func fetchWireGuardAPI(reqData apiRequest) (*apiResponse, error) {
+	apiUrl := os.Getenv("WG_STATS_API")
+	apiClient := http.Client{
+		Timeout: time.Second * 2, // Timeout after 2 seconds
+	}
+	jsonData, _ := json.Marshal(reqData)
+	req, err := http.NewRequest(http.MethodPost, apiUrl, bytes.NewBuffer(jsonData))
+	if err != nil {
+		return nil, err
+	}
+
+	req.Header.Set("User-Agent", "wg-gen-web")
+	req.Header.Set("Accept", "application/json")
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("Cache-Control", "no-cache")
+
+	if os.Getenv("WG_STATS_API_USER") != "" {
+		req.SetBasicAuth(os.Getenv("WG_STATS_API_USER"), os.Getenv("WG_STATS_API_PASS"))
+	}
+
+	res, getErr := apiClient.Do(req)
+	if getErr != nil {
+		return nil, getErr
+	}
+
+	if res.Body != nil {
+		defer res.Body.Close()
+	}
+
+	body, readErr := ioutil.ReadAll(res.Body)
+	if readErr != nil {
+		return nil, readErr
+	}
+
+	response := apiResponse{}
+	jsonErr := json.Unmarshal(body, &response)
+	if jsonErr != nil {
+		return nil, jsonErr
+	}
+
+	return &response, nil
+}
+
+// ReadInterfaceStatus object, create default one
+func ReadInterfaceStatus() (*model.InterfaceStatus, error) {
+	interfaceStatus := &model.InterfaceStatus{
+		Name:          "unknown",
+		DeviceType:    "unknown",
+		ListenPort:    0,
+		NumberOfPeers: 0,
+		PublicKey:     "",
+	}
+
+	data, err := fetchWireGuardAPI(apiRequest{
+		Version: "2.0",
+		Method:  "GetDeviceInfo",
+		Params:  nil,
+	})
+	if err != nil {
+		return interfaceStatus, err
+	}
+
+	resultData := data.Result.(map[string]interface{})
+	device := resultData["device"].(map[string]interface{})
+	interfaceStatus.Name = device["name"].(string)
+	interfaceStatus.DeviceType = device["type"].(string)
+	interfaceStatus.PublicKey = device["public_key"].(string)
+	interfaceStatus.ListenPort = int(device["listen_port"].(float64))
+	interfaceStatus.NumberOfPeers = int(device["num_peers"].(float64))
+
+	return interfaceStatus, nil
+}
+
+// ReadClientStatus object, create default one, last recent active client is listed first
+func ReadClientStatus() ([]*model.ClientStatus, error) {
+	var clientStatus []*model.ClientStatus
+
+	data, err := fetchWireGuardAPI(apiRequest{
+		Version: "2.0",
+		Method:  "ListPeers",
+		Params:  []byte("{}"),
+	})
+	if err != nil {
+		return clientStatus, err
+	}
+
+	resultData := data.Result.(map[string]interface{})
+	peers := resultData["peers"].([]interface{})
+
+	clients, err := ReadClients()
+	withClientDetails := true
+	if err != nil {
+		withClientDetails = false
+	}
+
+	for _, tmpPeer := range peers {
+		peer := tmpPeer.(map[string]interface{})
+		peerHandshake, _ := time.Parse(time.RFC3339Nano, peer["last_handshake"].(string))
+		peerIPs := peer["allowed_ips"].([]interface{})
+		peerAddresses := make([]string, len(peerIPs))
+		for i, peerIP := range peerIPs {
+			peerAddresses[i] = peerIP.(string)
+		}
+
+		newClientStatus := &model.ClientStatus{
+			PublicKey:        peer["public_key"].(string),
+			HasPresharedKey:  peer["has_preshared_key"].(bool),
+			ProtocolVersion:  int(peer["protocol_version"].(float64)),
+			Name:             "UNKNOWN",
+			Email:            "UNKNOWN",
+			Connected:        false,
+			AllowedIPs:       peerAddresses,
+			Endpoint:         peer["endpoint"].(string),
+			LastHandshake:    peerHandshake,
+			ReceivedBytes:    int(peer["receive_bytes"].(float64)),
+			TransmittedBytes: int(peer["transmit_bytes"].(float64)),
+		}
+
+		if withClientDetails {
+			for _, client := range clients {
+				if client.PublicKey != newClientStatus.PublicKey {
+					continue
+				}
+
+				newClientStatus.Name = client.Name
+				newClientStatus.Email = client.Email
+				break
+			}
+		}
+
+		clientStatus = append(clientStatus, newClientStatus)
+	}
+	return clientStatus, nil
+}
diff --git a/model/status.go b/model/status.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f63917717250214d75b0c444dbadf343757a6d7
--- /dev/null
+++ b/model/status.go
@@ -0,0 +1,29 @@
+package model
+
+import (
+	"time"
+)
+
+// ClientStatus structure
+type ClientStatus struct {
+	PublicKey        string    `json:"publicKey"`
+	HasPresharedKey  bool      `json:"hasPresharedKey"`
+	ProtocolVersion  int       `json:"protocolVersion"`
+	Name             string    `json:"name"`
+	Email            string    `json:"email"`
+	Connected        bool      `json:"connected"`
+	AllowedIPs       []string  `json:"allowedIPs"`
+	Endpoint         string    `json:"endpoint"`
+	LastHandshake    time.Time `json:"lastHandshake"`
+	ReceivedBytes    int       `json:"receivedBytes"`
+	TransmittedBytes int       `json:"transmittedBytes"`
+}
+
+// InterfaceStatus structure
+type InterfaceStatus struct {
+	Name          string `json:"name"`
+	DeviceType    string `json:"type"`
+	ListenPort    int    `json:"listenPort"`
+	NumberOfPeers int    `json:"numPeers"`
+	PublicKey     string `json:"publicKey"`
+}
diff --git a/ui/src/components/Header.vue b/ui/src/components/Header.vue
index 58ca18fa712add7492071ab0d89928f1f6a2cf1a..e3645449bf38c0fb1b2b3a0ea3e7cce38145794d 100644
--- a/ui/src/components/Header.vue
+++ b/ui/src/components/Header.vue
@@ -15,6 +15,10 @@
                     Server
                     <v-icon right dark>mdi-vpn</v-icon>
                 </v-btn>
+                <v-btn to="/status">
+                    Status
+                    <v-icon right dark>mdi-chart-bar</v-icon>
+                </v-btn>
             </v-toolbar-items>
 
             <v-menu
diff --git a/ui/src/components/Status.vue b/ui/src/components/Status.vue
new file mode 100644
index 0000000000000000000000000000000000000000..88633f02f4aaf46a122cfc1fa9f1b7f825ffe34e
--- /dev/null
+++ b/ui/src/components/Status.vue
@@ -0,0 +1,175 @@
+<template>
+    <v-container>
+      <v-row v-if="dataLoaded">
+        <v-col cols="12">
+          <v-card>
+            <v-card-title>
+              WireGuard Interface Status: {{ interface.name }}
+            </v-card-title>
+            <v-list-item>
+              <v-list-item-content>
+                <v-list-item-subtitle>Public Key: {{ interface.publicKey }}</v-list-item-subtitle>
+                <v-list-item-subtitle>Listening Port: {{ interface.listenPort }}</v-list-item-subtitle>
+                <v-list-item-subtitle>Device Type: {{ interface.type }}</v-list-item-subtitle>
+                <v-list-item-subtitle>Number of Peers: {{ interface.numPeers }}</v-list-item-subtitle>
+              </v-list-item-content>
+            </v-list-item>
+          </v-card>
+        </v-col>
+      </v-row>
+      <v-row v-if="dataLoaded">
+        <v-col cols="12">
+          <v-card>
+            <v-card-title>
+              WireGuard Client Status
+              <v-spacer></v-spacer>
+              <v-text-field
+                  v-model="search"
+                  append-icon="mdi-magnify"
+                  label="Search"
+                  single-line
+                  hide-details
+              ></v-text-field>
+              <v-spacer></v-spacer>
+              <v-btn
+                  color="success"
+                  @click="reload"
+              >
+                Reload
+                <v-icon right dark>mdi-reload</v-icon>
+              </v-btn>
+            </v-card-title>
+            <v-data-table
+              :headers="headers"
+              :items="clients"
+              :search="search"
+          >
+            <template v-slot:item.address="{ item }">
+              <v-chip
+                  v-for="(ip, i) in item.address"
+                  :key="i"
+                  color="indigo"
+                  text-color="white"
+              >
+                <v-icon left>mdi-ip-network</v-icon>
+                {{ ip }}
+              </v-chip>
+            </template>
+            <template v-slot:item.tags="{ item }">
+              <v-chip
+                  v-for="(tag, i) in item.tags"
+                  :key="i"
+                  color="blue-grey"
+                  text-color="white"
+              >
+                <v-icon left>mdi-tag</v-icon>
+                {{ tag }}
+              </v-chip>
+            </template>
+            <template v-slot:item.created="{ item }">
+              <v-row>
+                <p>At {{ item.created | formatDate }} by {{ item.createdBy }}</p>
+              </v-row>
+            </template>
+            <template v-slot:item.updated="{ item }">
+              <v-row>
+                <p>At {{ item.updated | formatDate }} by {{ item.updatedBy }}</p>
+              </v-row>
+            </template>
+            <template v-slot:item.action="{ item }">
+              <v-row>
+                <v-icon
+                    class="pr-1 pl-1"
+                    @click.stop="startUpdate(item)"
+                >
+                  mdi-square-edit-outline
+                </v-icon>
+                <v-icon
+                    class="pr-1 pl-1"
+                    @click.stop="forceFileDownload(item)"
+                >
+                  mdi-cloud-download-outline
+                </v-icon>
+                <v-icon
+                    class="pr-1 pl-1"
+                    @click.stop="email(item)"
+                >
+                  mdi-email-send-outline
+                </v-icon>
+                <v-icon
+                    class="pr-1 pl-1"
+                    @click="remove(item)"
+                >
+                  mdi-trash-can-outline
+                </v-icon>
+                <v-switch
+                    dark
+                    class="pr-1 pl-1"
+                    color="success"
+                    v-model="item.enable"
+                    v-on:change="update(item)"
+                />
+              </v-row>
+            </template>
+
+          </v-data-table>
+          </v-card>
+        </v-col>
+      </v-row>
+      <v-row v-else>
+        <v-col cols="12">
+          <v-card>
+            <v-card-title>
+              No stats available...
+            </v-card-title>
+            <v-card-text>{{ error }}</v-card-text>
+          </v-card>
+        </v-col>
+      </v-row>
+    </v-container>
+</template>
+<script>
+  import { mapActions, mapGetters } from 'vuex'
+
+  export default {
+    name: 'Status',
+
+    data: () => ({
+      search: '',
+      headers: [
+        { text: 'Connected', value: 'connected', },
+        { text: 'Name', value: 'name', },
+        { text: 'Endpoint', value: 'endpoint', },
+        { text: 'IP addresses', value: 'allowedIPs', sortable: false, },
+        { text: 'Received Bytes', value: 'receivedBytes', },
+        { text: 'Transmitted Bytes', value: 'transmittedBytes', },
+        { text: 'Last Handshake', value: 'lastHandshake',} ,
+      ],
+    }),
+
+    computed:{
+      ...mapGetters({
+        interface: 'status/interfaceStatus',
+        clients: 'status/clientStatus',
+        error: 'status/error',
+      }),
+      dataLoaded: function () {
+        return this.interface != null && this.interface.name !== "";
+      }
+    },
+
+    mounted () {
+      this.readStatus()
+    },
+
+    methods: {
+      ...mapActions('status', {
+        readStatus: 'read',
+      }),
+
+      reload() {
+        this.readStatus()
+      },
+    }
+  };
+</script>
diff --git a/ui/src/router/index.js b/ui/src/router/index.js
index ee29359639b949ce6a018767c37c9931482b6c85..c4c1d414feb012ec6042a63cad13858515aacb0c 100644
--- a/ui/src/router/index.js
+++ b/ui/src/router/index.js
@@ -24,7 +24,17 @@ const routes = [
     meta: {
       requiresAuth: true
     }
-  }
+  },
+  {
+    path: '/status',
+    name: 'status',
+    component: function () {
+      return import(/* webpackChunkName: "Status" */ '../views/Status.vue')
+    },
+    meta: {
+      requiresAuth: true
+    }
+  },
 ];
 
 const router = new VueRouter({
diff --git a/ui/src/store/index.js b/ui/src/store/index.js
index 97b5b767ebbfaf23cac1b4503bc1589ca0c627dc..ea685b8d27c2d173824de7826dd65ea63e4fcbea 100644
--- a/ui/src/store/index.js
+++ b/ui/src/store/index.js
@@ -3,6 +3,7 @@ import Vuex from 'vuex'
 import auth from "./modules/auth";
 import client from "./modules/client";
 import server from "./modules/server";
+import status from "./modules/status";
 
 Vue.use(Vuex)
 
@@ -14,6 +15,7 @@ export default new Vuex.Store({
   modules: {
     auth,
     client,
-    server
+    server,
+    status,
   }
 })
diff --git a/ui/src/store/modules/status.js b/ui/src/store/modules/status.js
new file mode 100644
index 0000000000000000000000000000000000000000..fef5cd9a7cc4b616d416cbd407ecf816d6af516a
--- /dev/null
+++ b/ui/src/store/modules/status.js
@@ -0,0 +1,77 @@
+import ApiService from "../../services/api.service";
+
+const state = {
+  error: null,
+  interfaceStatus: null,
+  clientStatus: [],
+  version: '_ci_build_not_run_properly_',
+}
+
+const getters = {
+  error(state) {
+    return state.error;
+  },
+
+  interfaceStatus(state) {
+    return state.interfaceStatus;
+  },
+
+  clientStatus(state) {
+    return state.clientStatus;
+  },
+
+  version(state) {
+    return state.version;
+  },
+}
+
+const actions = {
+  error({ commit }, error){
+    commit('error', error)
+  },
+
+  read({ commit }){
+    ApiService.get("/status/interface")
+      .then(resp => {
+        commit('interfaceStatus', resp)
+      })
+      .catch(err => {
+        commit('interfaceStatus', null);
+        commit('error', err)
+      });
+    ApiService.get("/status/clients")
+      .then(resp => {
+        commit('clientStatus', resp)
+      })
+      .catch(err => {
+        commit('clientStatus', []);
+        commit('error', err)
+      });
+  },
+}
+
+const mutations = {
+  error(state, error) {
+    state.error = error;
+  },
+
+  interfaceStatus(state, interfaceStatus){
+    state.interfaceStatus = interfaceStatus
+  },
+
+  clientStatus(state, clientStatus){
+    state.clientStatus = clientStatus
+  },
+
+  version(state, version){
+    state.version = version
+  },
+}
+
+export default {
+  namespaced: true,
+  state,
+  getters,
+  actions,
+  mutations
+}
diff --git a/ui/src/views/Status.vue b/ui/src/views/Status.vue
new file mode 100644
index 0000000000000000000000000000000000000000..7888cc61ce6b6352e3bb6084a0d4c2d9c6d3b6df
--- /dev/null
+++ b/ui/src/views/Status.vue
@@ -0,0 +1,16 @@
+<template>
+  <v-content>
+    <Status/>
+  </v-content>
+</template>
+
+<script>
+  import Status from '../components/Status'
+
+  export default {
+    name: 'status',
+    components: {
+      Status
+    }
+  }
+</script>