diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java
index 2998ff19a5d01088b06eae754935d3b9ae543147..ae86d980b2a59fb3d8f5d3a46f73fdcb434057c3 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/Plugin.java
@@ -27,7 +27,7 @@ import org.duniter.elasticsearch.rest.RestModule;
 import org.duniter.elasticsearch.security.SecurityModule;
 import org.duniter.elasticsearch.service.ServiceModule;
 import org.duniter.elasticsearch.threadpool.ThreadPool;
-import org.duniter.elasticsearch.websocket.WebsocketModule;
+import org.duniter.elasticsearch.websocket.WebSocketModule;
 import org.elasticsearch.common.component.LifecycleComponent;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.inject.Module;
@@ -66,7 +66,7 @@ public class Plugin extends org.elasticsearch.plugins.Plugin {
         }
         modules.add(new SecurityModule());
 
-        modules.add(new WebsocketModule());
+        modules.add(new WebSocketModule());
         modules.add(new RestModule());
 
         modules.add(new ServiceModule());
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
index 70e2f12bdb6402986ac52a9c60a81721801ee414..fe53f1c7ca20474ac70c0b5c9ecb02a67797af9e 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
@@ -281,10 +281,14 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
         return settings.getAsInt("duniter.ws.port", 9200);
     }
 
+    public boolean getWebSocketEnable()  {
+        return settings.getAsBoolean("duniter.ws.enable", Boolean.TRUE);
+    }
+
     /* protected methods */
 
     protected void initI18n() throws IOException {
-        if (I18n.getDefaultLocale() != null) return; // already init
+        //if (I18n.getDefaultLocale() != null) return; // already init
 
         // --------------------------------------------------------------------//
         // init i18n
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/DocumentNotFoundException.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/DocumentNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..338f7012b9aa3f42bfbfa7524ece3785d9801cab
--- /dev/null
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/exception/DocumentNotFoundException.java
@@ -0,0 +1,47 @@
+package org.duniter.elasticsearch.exception;
+
+/*
+ * #%L
+ * Duniter4j :: ElasticSearch Plugin
+ * %%
+ * Copyright (C) 2014 - 2016 EIS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the 
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/gpl-3.0.html>.
+ * #L%
+ */
+
+import org.elasticsearch.rest.RestStatus;
+
+/**
+ * Created by blavenie on 01/03/16.
+ */
+public class DocumentNotFoundException extends DuniterElasticsearchException {
+    public DocumentNotFoundException(Throwable cause) {
+        super(cause);
+    }
+
+    public DocumentNotFoundException(String msg, Object... args) {
+        super(msg, args);
+    }
+
+    public DocumentNotFoundException(String msg, Throwable cause, Object... args) {
+        super(msg, args, cause);
+    }
+
+    @Override
+    public RestStatus status() {
+        return RestStatus.BAD_REQUEST;
+    }
+}
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java
index f9486338ed64386fd830374ce604f373495af823..f00a564be16bb2032c1eb2d4a33f25d65faf1c6a 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java
@@ -29,6 +29,7 @@ import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import com.google.gson.JsonSyntaxException;
+import org.apache.commons.collections4.MapUtils;
 import org.duniter.core.beans.Bean;
 import org.duniter.core.client.model.elasticsearch.Record;
 import org.duniter.core.exception.TechnicalException;
@@ -147,7 +148,7 @@ public abstract class AbstractService implements Bean {
                 .execute().actionGet();
         return response.getId();
     }
-    protected void checkIssuerAndUpdateDocumentFromJson(String index, String type, String json, String id) {
+    protected void checkIssuerAndUpdateDocumentFromJson(String index, String type, String id, String json) {
 
         JsonNode actualObj = readAndVerifyIssuerSignature(json);
         String issuer = getIssuer(actualObj);
@@ -159,6 +160,10 @@ public abstract class AbstractService implements Bean {
             logger.debug(String.format("Updating %s [%s] from issuer [%s]", type, id, issuer.substring(0, 8)));
         }
 
+        updateDocumentFromJson(index, type, id, json);
+    }
+
+    protected void updateDocumentFromJson(String index, String type, String id, String json) {
         // Execute indexBlocksFromNode
         client.prepareUpdate(index, type, id)
                 .setDoc(json)
@@ -283,6 +288,20 @@ public abstract class AbstractService implements Bean {
         }
     }
 
+    /**
+     * Retrieve a field from a document id
+     * @param docId
+     * @return
+     */
+    protected Object getFieldById(String index, String type, String docId, String fieldName) {
+
+        Map<String, Object> result = getFieldsById(index, type, docId, fieldName);
+        if (MapUtils.isEmpty(result)) {
+            return null;
+        }
+        return result.get(fieldName);
+    }
+
     protected void bulkFromClasspathFile(String classpathFile, String indexName, String indexType) {
         bulkFromClasspathFile(classpathFile, indexName, indexType, null);
     }
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java
index 98d46240d912a81aee7fcd89f905d6cf9d45032f..23609da14728cbbdb23236312bbf14b34f5c0bb2 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/ServiceLocator.java
@@ -54,7 +54,7 @@ import java.io.IOException;
 public class ServiceLocator
         extends org.duniter.core.client.service.ServiceLocator
         {
-    private static final ESLogger logger = ESLoggerFactory.getLogger(ServiceLocator.class.getName());
+    private static final ESLogger logger = ESLoggerFactory.getLogger("duniter.service");
 
     private static BeanFactory beanFactory = null;
 
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketModule.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebSocketModule.java
similarity index 81%
rename from duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketModule.java
rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebSocketModule.java
index e3472356762aa94f1385fd79c357c252116addb2..717933a1ee9b1c47e5a7ec06b075a61e5a0b0c1f 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketModule.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebSocketModule.java
@@ -38,16 +38,13 @@ package org.duniter.elasticsearch.websocket;
     limitations under the License.
 */
 
+import org.duniter.elasticsearch.websocket.changes.WebSocketChangeEndPoint;
 import org.elasticsearch.common.inject.AbstractModule;
-import org.elasticsearch.common.logging.ESLogger;
-import org.elasticsearch.common.logging.Loggers;
 
-public class WebsocketModule extends AbstractModule {
-    private final ESLogger log = Loggers.getLogger(WebsocketModule.class);
-    
+public class WebSocketModule extends AbstractModule {
     @Override
     protected void configure() {
-        log.debug("Binding websocket Module");
-        bind(WebsocketServer.class).asEagerSingleton();
+        bind(WebSocketServer.class).asEagerSingleton();
+        bind(WebSocketChangeEndPoint.Init.class).asEagerSingleton();
     }
 }
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketServer.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebSocketServer.java
similarity index 65%
rename from duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketServer.java
rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebSocketServer.java
index f3a44ae8c07495699c3fe34ac374b307e3cf4bfb..598fc81f9dc16819a0493b2e19043969ce79c334 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketServer.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebSocketServer.java
@@ -38,7 +38,10 @@ package org.duniter.elasticsearch.websocket;
     limitations under the License.
 */
 
+import org.duniter.core.exception.TechnicalException;
 import org.duniter.elasticsearch.PluginSettings;
+import org.duniter.elasticsearch.threadpool.ThreadPool;
+import org.duniter.elasticsearch.websocket.changes.WebSocketChangeEndPoint;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.logging.ESLogger;
 import org.elasticsearch.common.logging.Loggers;
@@ -47,20 +50,45 @@ import org.glassfish.tyrus.server.Server;
 import javax.websocket.DeploymentException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
 
-public class WebsocketServer {
+public class WebSocketServer {
 
-    private final ESLogger log = Loggers.getLogger(WebsocketServer.class);
+    private final ESLogger log = Loggers.getLogger("duniter.ws");
+    private List<Class<?>> endPoints = new ArrayList<>();
 
     @Inject
-    public WebsocketServer(final PluginSettings pluginSettings) {
-        final String host = pluginSettings.getWebSocketHost();
-        final int port = pluginSettings.getWebSocketPort();
+    public WebSocketServer(final PluginSettings pluginSettings, ThreadPool threadPool) {
+        // If WS enable
+        if (pluginSettings.getWebSocketEnable()) {
+            // When node started
+            threadPool.scheduleOnStarted(() -> {
+                // start WS server
+                startServer(pluginSettings.getWebSocketHost(),
+                        pluginSettings.getWebSocketPort(),
+                        getEndPoints());
+            });
+        }
+    }
+
+
+    public void addEndPoint(Class<?> endPoint) {
+        endPoints.add(endPoint);
+    }
+
+    /* -- private medthod -- */
+
+    private Class[] getEndPoints() {
+        return endPoints.toArray(new Class<?>[endPoints.size()]);
+    }
+
+    private void startServer(String host, int port, Class<?>[] endPoints) {
 
-        final Server server = new Server(host, port, "/ws", null, WebsocketChangeEndPoint.class) ;
+        final Server server = new Server(host, port, "/ws", null, endPoints) ;
 
         try {
-            log.info("Starting websocket server");
+            log.info("Starting Websocket server...");
             AccessController.doPrivileged(new PrivilegedAction() {
                 @Override
                 public Object run() {
@@ -70,16 +98,16 @@ public class WebsocketServer {
                         // This is a workaround for that
                         Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
                         server.start();
+                        log.info("Websocket server started");
                         return null;
                     } catch (DeploymentException e) {
                         throw new RuntimeException("Failed to start server", e);
                     }
                 }
             });
-            log.info("Websocket server started");
         } catch (Exception e) {
-            log.error("Failed to start Websocket server",e);
-            throw new RuntimeException(e);
+            log.error("Failed to start Websocket server", e);
+            throw new TechnicalException(e);
         }
     }
 
diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketChangeEndPoint.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/changes/WebSocketChangeEndPoint.java
similarity index 79%
rename from duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketChangeEndPoint.java
rename to duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/changes/WebSocketChangeEndPoint.java
index 23e983af34e1c6a1d2e1088d11a2c4bf1fc9bdb7..b99e117e054800fb6cbfda1910f1ec34fb56e981 100644
--- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/WebsocketChangeEndPoint.java
+++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/websocket/changes/WebSocketChangeEndPoint.java
@@ -1,4 +1,4 @@
-package org.duniter.elasticsearch.websocket;
+package org.duniter.elasticsearch.websocket.changes;
 
 /*
  * #%L
@@ -40,6 +40,8 @@ package org.duniter.elasticsearch.websocket;
 
 import org.duniter.elasticsearch.service.changes.ChangeListener;
 import org.duniter.elasticsearch.service.changes.ChangeService;
+import org.duniter.elasticsearch.websocket.WebSocketServer;
+import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.logging.ESLogger;
 import org.elasticsearch.common.logging.Loggers;
 
@@ -47,14 +49,22 @@ import javax.websocket.*;
 import javax.websocket.server.ServerEndpoint;
 
 @ServerEndpoint(value = "/_changes")
-public class WebsocketChangeEndPoint implements ChangeListener{
+public class WebSocketChangeEndPoint implements ChangeListener{
 
-    private final ESLogger log = Loggers.getLogger(WebsocketChangeEndPoint.class);
+    public static class Init {
+
+        @Inject
+        public Init(WebSocketServer webSocketServer) {
+            webSocketServer.addEndPoint(WebSocketChangeEndPoint.class);
+        }
+    }
+
+    private final ESLogger log = Loggers.getLogger("duniter.ws.changes");
     private Session session;
 
     @OnOpen
     public void onOpen(Session session) {
-        log.info("Connected ... " + session.getId());
+        log.debug("Connected ... " + session.getId());
         this.session = session;
         ChangeService.registerListener(this);
     }
@@ -71,12 +81,12 @@ public class WebsocketChangeEndPoint implements ChangeListener{
 
     @OnMessage
     public void onMessage(String message) {
-        log.info("Received message: "+message);
+        log.debug("Received message: "+message);
     }
 
     @OnClose
     public void onClose(CloseReason reason) {
-        log.info("Closing websocket: "+reason);
+        log.debug("Closing websocket: "+reason);
         ChangeService.unregisterListener(this);
         this.session = null;
     }
diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginInit.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginInit.java
index 030c42e37f70bbf5514b86f58782103a6ae4b7c3..e148805a53f11ad599cb38a4a153396744f0fb4f 100644
--- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginInit.java
+++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/PluginInit.java
@@ -49,7 +49,7 @@ public class PluginInit extends AbstractLifecycleComponent<PluginInit> {
     private final static ESLogger logger = Loggers.getLogger("gchange");
 
     @Inject
-    public PluginInit(Client client, Settings settings, PluginSettings pluginSettings, ThreadPool threadPool, final Injector injector) {
+    public PluginInit(Settings settings, PluginSettings pluginSettings, ThreadPool threadPool, final Injector injector) {
         super(settings);
         this.pluginSettings = pluginSettings;
         this.threadPool = threadPool;
diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java
index 0ff97ce969d666e7ac3756df7549f4c959c7e930..95b97aafa02a919fb2fa6b23b83717b70cc5b7bc 100644
--- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java
+++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/MarketService.java
@@ -25,35 +25,28 @@ package org.duniter.elasticsearch.gchange.service;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
-import com.google.common.base.Joiner;
-import com.google.gson.JsonSyntaxException;
-import org.duniter.core.client.model.elasticsearch.Record;
+import org.apache.commons.collections4.MapUtils;
 import org.duniter.core.client.model.elasticsearch.RecordComment;
 import org.duniter.core.client.service.bma.WotRemoteService;
 import org.duniter.core.exception.TechnicalException;
 import org.duniter.core.service.CryptoService;
+import org.duniter.elasticsearch.exception.DocumentNotFoundException;
 import org.duniter.elasticsearch.gchange.PluginSettings;
 import org.duniter.elasticsearch.gchange.model.MarketRecord;
 import org.duniter.elasticsearch.gchange.model.event.GchangeEventCodes;
 import org.duniter.elasticsearch.service.AbstractService;
+import org.duniter.elasticsearch.user.service.UserService;
 import org.duniter.elasticsearch.user.service.event.UserEvent;
 import org.duniter.elasticsearch.user.service.event.UserEventLink;
 import org.duniter.elasticsearch.user.service.event.UserEventService;
 import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
 import org.elasticsearch.action.index.IndexRequestBuilder;
 import org.elasticsearch.action.index.IndexResponse;
-import org.elasticsearch.action.search.SearchPhaseExecutionException;
-import org.elasticsearch.action.search.SearchRequestBuilder;
-import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.action.search.SearchType;
 import org.elasticsearch.client.Client;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
-import org.elasticsearch.index.query.QueryBuilders;
-import org.elasticsearch.search.SearchHit;
-import org.elasticsearch.search.SearchHitField;
 import org.nuiton.i18n.I18n;
 
 import java.io.IOException;
@@ -73,15 +66,18 @@ public class MarketService extends AbstractService {
 
     private WotRemoteService wotRemoteService;
     private UserEventService userEventService;
+    private UserService userService;
 
     @Inject
     public MarketService(Client client, PluginSettings settings,
                          CryptoService cryptoService,
                          WotRemoteService wotRemoteService,
+                         UserService userService,
                          UserEventService userEventService) {
         super("gchange." + INDEX, client, settings, cryptoService);
         this.wotRemoteService = wotRemoteService;
         this.userEventService = userEventService;
+        this.userService = userService;
     }
 
     /**
@@ -170,38 +166,33 @@ public class MarketService extends AbstractService {
     }
 
     public String indexCommentFromJson(String json) {
-        JsonNode actualObj = readAndVerifyIssuerSignature(json);
-        String issuer = getMandatoryField(actualObj, RecordComment.PROPERTY_ISSUER).asText();
+        JsonNode commentObj = readAndVerifyIssuerSignature(json);
+        String issuer = getMandatoryField(commentObj, RecordComment.PROPERTY_ISSUER).asText();
 
         if (logger.isDebugEnabled()) {
             logger.debug(String.format("Indexing a %s from issuer [%s]", RECORD_COMMENT_TYPE, issuer.substring(0, 8)));
         }
         String commentId = indexDocumentFromJson(INDEX, RECORD_COMMENT_TYPE, json);
 
-        // Notification
-        {
-            // Notify issuer of record (is not same as comment writer)
-            String recordId = getMandatoryField(actualObj, RecordComment.PROPERTY_RECORD).asText();
-            Map<String, Object> recordFields = getRecordFieldsById(recordId, MarketRecord.PROPERTY_TITLE, MarketRecord.PROPERTY_ISSUER);
-            String recordIssuer = recordFields.get(MarketRecord.PROPERTY_ISSUER).toString();
-            String recordTitle = recordFields.get(MarketRecord.PROPERTY_TITLE).toString();
-            if (!issuer.equals(recordIssuer)) {
-                userEventService.notifyUser(recordIssuer,
-                        new UserEvent(UserEvent.EventType.INFO,
-                                GchangeEventCodes.NEW_COMMENT.name(),
-                                new UserEventLink(INDEX, RECORD_TYPE, recordId),
-                                I18n.n("duniter.market.event.newComment"),
-                                issuer, recordTitle
-                        )
-                );
-            }
-        }
+        // Notify record issuer
+        notifyRecordIssuerForComment(commentObj, true);
 
         return commentId;
     }
 
     public void updateCommentFromJson(String json, String id) {
-        checkIssuerAndUpdateDocumentFromJson(INDEX, RECORD_COMMENT_TYPE, json, id);
+        JsonNode commentObj = readAndVerifyIssuerSignature(json);
+
+        if (logger.isDebugEnabled()) {
+            String issuer = getMandatoryField(commentObj, RecordComment.PROPERTY_ISSUER).asText();
+            logger.debug(String.format("Indexing a %s from issuer [%s]", RECORD_COMMENT_TYPE, issuer.substring(0, 8)));
+        }
+
+        updateDocumentFromJson(INDEX, RECORD_COMMENT_TYPE, id, json);
+
+        // Notify record issuer
+        notifyRecordIssuerForComment(commentObj, false);
+
     }
 
     public MarketService fillRecordCategories() {
@@ -423,16 +414,34 @@ public class MarketService extends AbstractService {
         }
     }
 
-    /**
-     * Retrieve record field's values
-     * @param recordId
-     * @param fieldNames
-     * @return
-     */
-    protected Map<String, Object> getRecordFieldsById(String recordId, String... fieldNames) {
+    // Notification
+    private void notifyRecordIssuerForComment(JsonNode actualObj, boolean isNewComment) {
+        String issuer = getMandatoryField(actualObj, RecordComment.PROPERTY_ISSUER).asText();
 
-        return getFieldsById(INDEX, RECORD_TYPE, recordId, fieldNames);
+        // Notify issuer of record (is not same as comment writer)
+        String recordId = getMandatoryField(actualObj, RecordComment.PROPERTY_RECORD).asText();
+        Map<String, Object> recordFields = getFieldsById(INDEX, RECORD_TYPE, recordId,
+                MarketRecord.PROPERTY_TITLE, MarketRecord.PROPERTY_ISSUER);
+        if (MapUtils.isEmpty(recordFields)) { // record not found
+            throw new DocumentNotFoundException(I18n.t("duniter.market.error.comment.recordNotFound", recordId));
+        }
+        String recordIssuer = recordFields.get(MarketRecord.PROPERTY_ISSUER).toString();
+
+        // Get user title
+        String issuerTitle = userService.getProfileTitle(issuer);
+
+        String recordTitle = recordFields.get(MarketRecord.PROPERTY_TITLE).toString();
+        if (!issuer.equals(recordIssuer)) {
+            userEventService.notifyUser(recordIssuer,
+                    new UserEvent(UserEvent.EventType.INFO,
+                            GchangeEventCodes.NEW_COMMENT.name(),
+                            new UserEventLink(INDEX, RECORD_TYPE, recordId),
+                            I18n.n(isNewComment ? "duniter.market.event.newComment": "duniter.market.event.updateComment"),
+                            issuerTitle != null ? issuerTitle : issuer.substring(0, 8),
+                            recordTitle
+                    )
+            );
+        }
     }
 
-
 }
diff --git a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/RegistryService.java b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/RegistryService.java
index 3542cd47c0b9b2dc7f7357098e6359ed7ae3f122..7505b8f40ee0390b4c7f40702af740ce133305d2 100644
--- a/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/RegistryService.java
+++ b/duniter4j-es-gchange/src/main/java/org/duniter/elasticsearch/gchange/service/RegistryService.java
@@ -25,12 +25,20 @@ package org.duniter.elasticsearch.gchange.service;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.google.gson.Gson;
+import org.apache.commons.collections4.MapUtils;
 import org.duniter.core.client.model.bma.gson.GsonUtils;
+import org.duniter.core.client.model.elasticsearch.RecordComment;
 import org.duniter.core.client.service.bma.BlockchainRemoteService;
 import org.duniter.core.exception.TechnicalException;
 import org.duniter.core.service.CryptoService;
+import org.duniter.elasticsearch.exception.DocumentNotFoundException;
 import org.duniter.elasticsearch.gchange.PluginSettings;
+import org.duniter.elasticsearch.gchange.model.MarketRecord;
+import org.duniter.elasticsearch.gchange.model.event.GchangeEventCodes;
 import org.duniter.elasticsearch.service.AbstractService;
+import org.duniter.elasticsearch.user.service.event.UserEvent;
+import org.duniter.elasticsearch.user.service.event.UserEventLink;
+import org.duniter.elasticsearch.user.service.event.UserEventService;
 import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
 import org.elasticsearch.action.index.IndexRequestBuilder;
 import org.elasticsearch.action.index.IndexResponse;
@@ -38,8 +46,10 @@ import org.elasticsearch.client.Client;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
+import org.nuiton.i18n.I18n;
 
 import java.io.IOException;
+import java.util.Map;
 
 /**
  * Created by Benoit on 30/03/2015.
@@ -54,15 +64,18 @@ public class RegistryService extends AbstractService {
 
     private final Gson gson;
     private BlockchainRemoteService blockchainRemoteService;
+    private UserEventService userEventService;
 
     @Inject
     public RegistryService(Client client,
                            PluginSettings settings,
                            CryptoService cryptoService,
-                           BlockchainRemoteService blockchainRemoteService) {
+                           BlockchainRemoteService blockchainRemoteService,
+                            UserEventService userEventService) {
         super("gchange." + INDEX, client, settings, cryptoService);
         this.gson = GsonUtils.newBuilder().create();
         this.blockchainRemoteService = blockchainRemoteService;
+        this.userEventService = userEventService;
     }
 
     /**
@@ -136,6 +149,7 @@ public class RegistryService extends AbstractService {
 
     public String indexCommentFromJson(String json) {
         return checkIssuerAndIndexDocumentFromJson(INDEX, RECORD_COMMENT_TYPE, json);
+
     }
 
     public void updateCommentFromJson(String json, String id) {
diff --git a/duniter4j-es-user/pom.xml b/duniter4j-es-user/pom.xml
index d4f1f5651b7f30c470046df1cf94dcf91e589ec3..12a9e711bce4a8550a9a833cbd81a32a93801da1 100644
--- a/duniter4j-es-user/pom.xml
+++ b/duniter4j-es-user/pom.xml
@@ -90,7 +90,7 @@
                         <configuration>
                             <attach>true</attach>
                             <appendAssemblyId>false</appendAssemblyId>
-                            <finalName>${bundlePrefix}</finalName>
+                            <finalName>${project.artifactId}-${project.version}</finalName>
                             <descriptors>
                                 <descriptor>
                                     ${basedir}/src/main/assembly/plugin.xml
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/Plugin.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/Plugin.java
index 0736535c3b7bd4d2e31d1e30e39e6d5604c841b8..66e193964f8cbe75b5cd6890aa78b35a3db4e395 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/Plugin.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/Plugin.java
@@ -25,6 +25,7 @@ package org.duniter.elasticsearch.user;
 import com.google.common.collect.Lists;
 import org.duniter.elasticsearch.user.rest.RestModule;
 import org.duniter.elasticsearch.user.service.ServiceModule;
+import org.duniter.elasticsearch.user.websocket.WebSocketModule;
 import org.elasticsearch.common.component.LifecycleComponent;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.inject.Module;
@@ -64,6 +65,8 @@ public class Plugin extends org.elasticsearch.plugins.Plugin {
 
         modules.add(new RestModule());
         modules.add(new ServiceModule());
+        modules.add(new WebSocketModule());
+
 
         return modules;
     }
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java
index c0e597f17c485745f559be039da07857a4daf3cb..aeb251d9bf5927d7eb7266e3614ccea0f0530190 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/PluginInit.java
@@ -31,6 +31,8 @@ import org.duniter.elasticsearch.user.service.UserService;
 import org.duniter.elasticsearch.user.service.event.UserEvent;
 import org.duniter.elasticsearch.user.service.event.UserEventCodes;
 import org.duniter.elasticsearch.user.service.event.UserEventService;
+import org.duniter.elasticsearch.user.websocket.WebsocketUserEventEndPoint;
+import org.duniter.elasticsearch.websocket.WebSocketServer;
 import org.elasticsearch.cluster.health.ClusterHealthStatus;
 import org.elasticsearch.common.component.AbstractLifecycleComponent;
 import org.elasticsearch.common.inject.Inject;
@@ -110,9 +112,6 @@ public class PluginInit extends AbstractLifecycleComponent<org.duniter.elasticse
             injector.getInstance(UserService.class)
                     .deleteIndex()
                     .createIndexIfNotExists();
-            injector.getInstance(UserEventService.class)
-                    .deleteIndex()
-                    .createIndexIfNotExists();
 
 
             if (logger.isInfoEnabled()) {
@@ -126,7 +125,6 @@ public class PluginInit extends AbstractLifecycleComponent<org.duniter.elasticse
             injector.getInstance(HistoryService.class).createIndexIfNotExists();
             injector.getInstance(UserService.class).createIndexIfNotExists();
             injector.getInstance(MessageService.class).createIndexIfNotExists();
-            injector.getInstance(UserEventService.class).createIndexIfNotExists();
 
             if (logger.isInfoEnabled()) {
                 logger.info("Checking Duniter indices... [OK]");
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java
index 9977f5a3b10d562522b9cd1b35780c614943479f..cad585fd3ae3cabba668a2881d8d96758d42849b 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java
@@ -25,12 +25,14 @@ package org.duniter.elasticsearch.user.service;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
+import org.duniter.core.client.model.elasticsearch.UserProfile;
 import org.duniter.core.exception.TechnicalException;
 import org.duniter.core.service.CryptoService;
 import org.duniter.core.service.MailService;
 import org.duniter.elasticsearch.PluginSettings;
 import org.duniter.elasticsearch.exception.AccessDeniedException;
 import org.duniter.elasticsearch.service.AbstractService;
+import org.duniter.elasticsearch.user.service.event.UserEventService;
 import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
 import org.elasticsearch.action.index.IndexResponse;
 import org.elasticsearch.action.update.UpdateResponse;
@@ -91,6 +93,7 @@ public class UserService extends AbstractService {
         createIndexRequestBuilder.setSettings(indexSettings);
         createIndexRequestBuilder.addMapping(PROFILE_TYPE, createProfileType());
         createIndexRequestBuilder.addMapping(SETTINGS_TYPE, createSettingsType());
+        createIndexRequestBuilder.addMapping(UserEventService.EVENT_TYPE, UserEventService.createEventType());
         createIndexRequestBuilder.execute().actionGet();
 
         return this;
@@ -194,6 +197,12 @@ public class UserService extends AbstractService {
     }
 
 
+    public String getProfileTitle(String issuer) {
+
+        Object title = getFieldById(INDEX, PROFILE_TYPE, issuer, UserProfile.PROPERTY_TITLE);
+        if (title == null) return null;
+        return title.toString();
+    }
 
     /* -- Internal methods -- */
 
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEvent.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEvent.java
index f54954a64385e4dcc1247e43ffe89d0d96d84eb5..e58ccbd2231dfebcd48c36777508aa03719e713d 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEvent.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEvent.java
@@ -84,6 +84,10 @@ public class UserEvent {
         return time;
     }
 
+    public UserEventLink getLink() {
+        return link;
+    }
+
     public enum EventType {
         INFO,
         WARN,
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventListener.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventListener.java
index 7d34b28bb45e7e0d8ca85a632a746fdf00eece72..9238e4a83ca0ea2fbc3d1b090ba2c5a7dd1e4fb8 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventListener.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventListener.java
@@ -2,5 +2,6 @@ package org.duniter.elasticsearch.user.service.event;
 
 public interface UserEventListener {
     String getId();
+    String getPubkey();
     void onEvent(UserEvent event);
 }
\ No newline at end of file
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventService.java
index e3cfda044265163c853c26f6271e2ca7130913c8..6d412001e432585d27bb8b6642936efae1e98066 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventService.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventService.java
@@ -57,7 +57,7 @@ import java.util.Map;
 /**
  * Created by Benoit on 30/03/2015.
  */
-public class UserEventService extends AbstractService implements ChangeListener {
+public class UserEventService extends AbstractService {
 
     public static final String INDEX = "user";
     public static final String EVENT_TYPE = "event";
@@ -89,7 +89,6 @@ public class UserEventService extends AbstractService implements ChangeListener
         if (!this.mailEnable && logger.isTraceEnabled()) {
             logger.trace("Mail disable");
         }
-        //ChangeService.registerListener(this);
     }
 
     /**
@@ -128,87 +127,16 @@ public class UserEventService extends AbstractService implements ChangeListener
         }, TimeValue.timeValueMillis(100));
     }
 
-    @Override
-    public void onChanges(String json) {
-        // TODO get doc issuer
-       /* String issuer = nodePubkey;
-
-        ChangeEvent event = ChangeUtils.fromJson(objectMapper, json);
-
-        // Skip event itself (avoid recursive call)
-        if (event.getIndex().equals(INDEX) && event.getType().equals(EVENT_TYPE)) {
-            return;
-        }
-
-        if (event.getOperation() == ChangeEvent.Operation.CREATE) {
-            notifyNewDocument(event.getIndex(), event.getType(), event.getId(), issuer);
-        }*/
-
-    }
-
-    @Override
-    public String getId() {
-        return "UserEventService";
-    }
-
-    /**
-     * Delete blockchain index, and all data
-     */
-    public UserEventService deleteIndex() {
-        deleteIndexIfExists(INDEX);
-        return this;
-    }
-
-    public boolean existsIndex() {
-        return super.existsIndex(INDEX);
-    }
-
-    /**
-     * Create index need for blockchain registry, if need
-     */
-    public UserEventService createIndexIfNotExists() {
-        try {
-            if (!existsIndex(INDEX)) {
-                createIndex();
-            }
-        }
-        catch(JsonProcessingException e) {
-            throw new TechnicalException(String.format("Error while creating index [%s]", INDEX));
-        }
-
-        return this;
-    }
-
-    /**
-     * Create index need for category registry
-     * @throws JsonProcessingException
-     */
-    public UserEventService createIndex() throws JsonProcessingException {
-        logger.info(String.format("Creating index [%s/%s]", INDEX, EVENT_TYPE));
-
-        CreateIndexRequestBuilder createIndexRequestBuilder = client.admin().indices().prepareCreate(INDEX);
-        Settings indexSettings = Settings.settingsBuilder()
-                .put("number_of_shards", 2)
-                .put("number_of_replicas", 1)
-                //.put("analyzer", createDefaultAnalyzer())
-                .build();
-        createIndexRequestBuilder.setSettings(indexSettings);
-        createIndexRequestBuilder.addMapping(EVENT_TYPE, createEventType());
-        createIndexRequestBuilder.execute().actionGet();
-
-        return this;
-    }
-
     public String indexEvent(String recipient, Locale locale, UserEvent event) {
         // Generate json
         String eventJson;
         if (StringUtils.isNotBlank(nodePubkey)) {
-            eventJson = toJson(nodePubkey, recipient, locale, event, null);
+            eventJson = UserEventUtils.toJson(nodePubkey, recipient, locale, event, null);
             String signature = cryptoService.sign(eventJson, nodeKeyPair.getSecKey());
-            eventJson = toJson(nodePubkey, recipient, locale, event, signature);
+            eventJson = UserEventUtils.toJson(nodePubkey, recipient, locale, event, signature);
         } else {
             // Node has not keyring : TODO no issuer ?
-            eventJson = toJson(recipient, recipient, locale, event, null);
+            eventJson = UserEventUtils.toJson(recipient, recipient, locale, event, null);
         }
 
         if (logger.isDebugEnabled()) {
@@ -246,7 +174,7 @@ public class UserEventService extends AbstractService implements ChangeListener
 
     /* -- Internal methods -- */
 
-    public XContentBuilder createEventType() {
+    public static XContentBuilder createEventType() {
         try {
             XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject(EVENT_TYPE)
                     .startObject("properties")
@@ -350,29 +278,7 @@ public class UserEventService extends AbstractService implements ChangeListener
         }
     }
 
-    private String toJson(String issuer, String recipient, Locale locale, UserEvent event, String signature) {
-        try {
-            XContentBuilder eventObject = XContentFactory.jsonBuilder().startObject()
-                    .field("type", event.getType().name())
-                    .field("issuer", issuer) // TODO isuer = node pubkey
-                    .field("recipient", recipient)
-                    .field("time", event.getTime())
-                    .field("code", event.getCode())
-                    .field("message", event.getLocalizedMessage(locale));
-            if (CollectionUtils.isNotEmpty(event.getParams())) {
-                eventObject.array("params", event.getParams());
-            }
-            if (StringUtils.isNotBlank(signature)) {
-                eventObject.field("signature", signature);
-            }
-            eventObject.endObject();
-            return eventObject.string();
-        }
-        catch(IOException e) {
-            throw new TechnicalException(e);
-        }
 
-    }
 
     private KeyPair getNodeKeyPairOrNull(PluginSettings pluginSettings) {
 
@@ -402,6 +308,7 @@ public class UserEventService extends AbstractService implements ChangeListener
         indexEvent(recipient, locale, event);
 
         // Send email to user
+        // TODO : group email by day ?
         if (StringUtils.isNotBlank(email)) {
             String subjectPrefix = pluginSettings.getMailSubjectPrefix();
             sendEmail(email,
@@ -410,7 +317,9 @@ public class UserEventService extends AbstractService implements ChangeListener
         }
 
         for (UserEventListener listener: LISTENERS.values()) {
-            listener.onEvent(event);
+            if (recipient.equals(listener.getPubkey())) {
+                listener.onEvent(event);
+            }
         }
     }
 }
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventUtils.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..20f2ed4d8c0e7b4ae1c929209848e3efd841a6d3
--- /dev/null
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/event/UserEventUtils.java
@@ -0,0 +1,84 @@
+package org.duniter.elasticsearch.user.service.event;
+
+import org.duniter.core.exception.TechnicalException;
+import org.duniter.core.util.CollectionUtils;
+import org.duniter.core.util.StringUtils;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * Created by blavenie on 02/12/16.
+ */
+public abstract class UserEventUtils {
+
+    public static String toJson(String issuer, String recipient, Locale locale, UserEvent event, String signature) {
+        try {
+            XContentBuilder eventObject = XContentFactory.jsonBuilder().startObject()
+                    .field("type", event.getType().name())
+                    .field("issuer", issuer) // TODO isuer = node pubkey
+                    .field("recipient", recipient)
+                    .field("time", event.getTime())
+                    .field("code", event.getCode())
+                    .field("message", event.getLocalizedMessage(locale));
+            if (CollectionUtils.isNotEmpty(event.getParams())) {
+                eventObject.array("params", event.getParams());
+            }
+
+            // Link
+            UserEventLink link = event.getLink();
+            if (link != null) {
+                eventObject.startObject("link")
+                        .field("index", link.getIndex())
+                        .field("type", link.getType());
+                if (StringUtils.isNotBlank(link.getId())) {
+                    eventObject.field("id", link.getId());
+                }
+                eventObject.endObject();
+            }
+
+            if (StringUtils.isNotBlank(signature)) {
+                eventObject.field("signature", signature);
+            }
+            eventObject.endObject();
+            return eventObject.string();
+        }
+        catch(IOException e) {
+            throw new TechnicalException(e);
+        }
+
+    }
+
+    public static String toJson(Locale locale, UserEvent event) {
+        try {
+            XContentBuilder eventObject = XContentFactory.jsonBuilder().startObject()
+                    .field("type", event.getType().name())
+                    .field("time", event.getTime())
+                    .field("code", event.getCode())
+                    .field("message", event.getLocalizedMessage(locale));
+            if (CollectionUtils.isNotEmpty(event.getParams())) {
+                eventObject.array("params", event.getParams());
+            }
+
+            // Link
+            UserEventLink link = event.getLink();
+            if (link != null) {
+                eventObject.startObject("link")
+                        .field("index", link.getIndex())
+                        .field("type", link.getType());
+                if (StringUtils.isNotBlank(link.getId())) {
+                    eventObject.field("id", link.getId());
+                }
+                eventObject.endObject();
+            }
+            eventObject.endObject();
+            return eventObject.string();
+        }
+        catch(IOException e) {
+            throw new TechnicalException(e);
+        }
+
+    }
+}
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebSocketModule.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebSocketModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..3aaf2d8ed86ccae793a1f76a785cea8d0c7500eb
--- /dev/null
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebSocketModule.java
@@ -0,0 +1,37 @@
+package org.duniter.elasticsearch.user.websocket;
+
+/*
+ * #%L
+ * duniter4j-elasticsearch-plugin
+ * %%
+ * Copyright (C) 2014 - 2016 EIS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the 
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/gpl-3.0.html>.
+ * #L%
+ */
+
+import org.elasticsearch.common.inject.AbstractModule;
+import org.elasticsearch.common.inject.Module;
+
+public class WebSocketModule extends AbstractModule implements Module {
+
+    @Override protected void configure() {
+        bind(WebsocketUserEventEndPoint.Init.class).asEagerSingleton();
+    }
+
+
+    /* protected methods */
+
+}
\ No newline at end of file
diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebsocketUserEventEndPoint.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebsocketUserEventEndPoint.java
index 7591f42997c4c387f07585d68784edb9348a145f..2c01b7ef93c35b42183a5a799df1a32353ee15b9 100644
--- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebsocketUserEventEndPoint.java
+++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/websocket/WebsocketUserEventEndPoint.java
@@ -38,21 +38,49 @@ package org.duniter.elasticsearch.user.websocket;
     limitations under the License.
 */
 
-//@ServerEndpoint(value = "/event/user/{pubkey}")
-public class WebsocketUserEventEndPoint /*implements UserEventListener*/ {
+import org.duniter.core.client.model.bma.Constants;
+import org.duniter.core.util.StringUtils;
+import org.duniter.elasticsearch.user.service.event.UserEvent;
+import org.duniter.elasticsearch.user.service.event.UserEventListener;
+import org.duniter.elasticsearch.user.service.event.UserEventService;
+import org.duniter.elasticsearch.user.service.event.UserEventUtils;
+import org.duniter.elasticsearch.websocket.WebSocketServer;
+import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.logging.ESLogger;
+import org.elasticsearch.common.logging.Loggers;
+import org.nuiton.i18n.I18n;
+
+import javax.websocket.*;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+@ServerEndpoint(value = "/event/user/{pubkey}/{locale}")
+public class WebsocketUserEventEndPoint implements UserEventListener {
+
+    public static class Init {
+
+        @Inject
+        public Init(WebSocketServer webSocketServer) {
+            webSocketServer.addEndPoint(WebsocketUserEventEndPoint.class);
+        }
+    }
 
-  /*  private static final String PATH_PARAM_PUBKEY = "pubkey";
+    private static final String PATH_PARAM_PUBKEY = "pubkey";
+    private static final String PATH_PARAM_LOCALE = "locale";
     private final static Pattern PUBKEY_PATTERN = Pattern.compile(Constants.Regex.PUBKEY);
 
     private final ESLogger log = Loggers.getLogger("duniter.ws.user.event");
     private Session session;
     private String pubkey;
-
+    private Locale locale;
 
     @OnOpen
     public void onOpen(Session session) {
         this.session = session;
         this.pubkey = session.getPathParameters() != null ? session.getPathParameters().get(PATH_PARAM_PUBKEY) : null;
+        this.locale = new Locale(session.getPathParameters() != null ? session.getPathParameters().get(PATH_PARAM_LOCALE) : "fr");
 
         if (StringUtils.isBlank(pubkey) || !PUBKEY_PATTERN.matcher(pubkey).matches()) {
             try {
@@ -63,13 +91,13 @@ public class WebsocketUserEventEndPoint /*implements UserEventListener*/ {
             return;
         }
 
-        log.info("User [%s] connecting with id [%s]", session.getId());
+        log.debug(I18n.t("duniter4j.ws.user.open", pubkey, session.getId(), locale.toString()));
         UserEventService.registerListener(this);
     }
 
     @Override
     public void onEvent(UserEvent event) {
-        session.getAsyncRemote().sendText("{\"type\":\""+event.getType().name()+"\",\"message\":\"" + event.getMessage() + "\"}");
+        session.getAsyncRemote().sendText(UserEventUtils.toJson(locale, event));
     }
 
     @Override
@@ -77,14 +105,19 @@ public class WebsocketUserEventEndPoint /*implements UserEventListener*/ {
         return session == null ? null : session.getId();
     }
 
+    @Override
+    public String getPubkey() {
+        return pubkey;
+    }
+
     @OnMessage
     public void onMessage(String message) {
-        log.info("Received message: "+message);
+        log.debug("Received message: "+message);
     }
 
     @OnClose
     public void onClose(CloseReason reason) {
-        log.info("Closing websocket: "+reason);
+        log.debug("Closing websocket: "+reason);
         UserEventService.unregisterListener(this);
         this.session = null;
     }
@@ -93,6 +126,5 @@ public class WebsocketUserEventEndPoint /*implements UserEventListener*/ {
     public void onError(Throwable t) {
         log.error("Error on websocket "+(session == null ? null : session.getId()), t);
     }
-*/
 
 }
diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
index 7c2d2a95ba12d23aa03f5f289a2672561c9ee450..d884f6badf7a9c692748e6d5d805ef10e31d99b9 100644
--- a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
+++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties
@@ -3,3 +3,4 @@ duniter4j.event.NODE_STARTED=Node started on cluster Duniter4j ES [%s]
 duniter4j.event.subject.ERROR=[%s] Error message
 duniter4j.event.subject.INFO=[%s] Information message
 duniter4j.event.subject.WARN=[%s] Warning message
+duniter4j.ws.user.open=User [%s] connecting with id [%s] with locale [%s]
diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
index 3ae4dd7f9b020c5836702fd8a2e4d7c0f0196572..e179adfd2247854f9bdda7bd936d2e3281d3218e 100644
--- a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
+++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties
@@ -3,3 +3,4 @@ duniter4j.event.NODE_STARTED=Noeud démarré sur le cluster Duniter4j ES [%s]
 duniter4j.event.subject.ERROR=%s Message d'erreur
 duniter4j.event.subject.INFO=%s Message d'information
 duniter4j.event.subject.WARN=%s Message d'avertissement
+duniter4j.ws.user.open=Utilisateur [%s] connecté id\=[%s] sur la locale [%s]