diff --git a/cesium-plus-pod-assembly/src/test/es-home/config/logging.yml b/cesium-plus-pod-assembly/src/test/es-home/config/logging.yml
index 9963d24c4caa4f672f11dcd7113bc22fe81bb61c..033d8a1510bddfe11a21074467be9dcf374ea202 100644
--- a/cesium-plus-pod-assembly/src/test/es-home/config/logging.yml
+++ b/cesium-plus-pod-assembly/src/test/es-home/config/logging.yml
@@ -16,7 +16,7 @@ logger:
 
   duniter: DEBUG
   #duniter.core: DEBUG
-  #duniter.security: ERROR
+  duniter.security: WARN
   #duniter.user.event: DEBUG
   #duniter.network.p2p: DEBUG
   #duniter.network.peer: DEBUG
diff --git a/cesium-plus-pod-core/pom.xml b/cesium-plus-pod-core/pom.xml
index 957f5f8b390432f0b09056a1ed0bf34098b0b20e..009d4c7fa02c53fec52a1fe7e9e026a2892b5ad3 100644
--- a/cesium-plus-pod-core/pom.xml
+++ b/cesium-plus-pod-core/pom.xml
@@ -79,7 +79,7 @@
 
     <dependency>
       <groupId>org.antlr</groupId>
-      <artifactId>stringtemplate</artifactId>
+      <artifactId>ST4</artifactId>
       <version>${stringtemplate.version}</version>
       <scope>compile</scope>
     </dependency>
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/PluginInit.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/PluginInit.java
index f9c4b8413aa995511a4ae84d99dede041b6371ea..969c6f8884ca0f516a0da8984e6bdec713d80053 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/PluginInit.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/PluginInit.java
@@ -152,6 +152,10 @@ public class PluginInit extends AbstractLifecycleComponent<PluginInit> {
             }
 
             final String currencyName = currency.getCurrencyName();
+            peer.setCurrency(currencyName);
+
+            // Define the main peer for this currency (will fill a cache in PeerService)
+            injector.getInstance(PeerService.class).setCurrencyMainPeer(currencyName, peer);
 
             // Add access security rules, for the currency indices
             injector.getInstance(RestSecurityController.class)
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
index a8b04f7c257440f64d7970d48a30f4d7f6a0ce29..be1812a4799437e6dfae66fe66ca847afc5474a9 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/PluginSettings.java
@@ -286,11 +286,25 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
         return this.settings.getAsBoolean("duniter.p2p.peering.enable", enableSynchro());
     }
 
+    /**
+     * Peer endpoint API to index (into the '_currency_/peer')
+     * @return
+     */
+    public Collection<EndpointApi> getPeerIndexedApis() {
+        String[] includeApis = settings.getAsArray("duniter.p2p.peer.indexedApis");
+        // By default: getPeeringPublishedApis + getPeeringTargetedApis
+        if (CollectionUtils.isEmpty(includeApis)) {
+            return CollectionUtils.union(getPeeringTargetedApis(), getPeeringPublishedApis());
+        }
+
+        return Arrays.stream(includeApis).map(EndpointApi::valueOf).collect(Collectors.toList());
+    }
+
     /**
      * Endpoint API to publish, in the emitted peer document. By default, plugins will defined their own API
      * @return
      */
-    public List<EndpointApi> getPeeringPublishedApis() {
+    public Collection<EndpointApi> getPeeringPublishedApis() {
         String[] targetedApis = settings.getAsArray("duniter.p2p.peering.publishedApis");
         if (CollectionUtils.isEmpty(targetedApis)) return null;
 
@@ -302,7 +316,7 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
      * This API should accept a POST request to '/network/peering' (like Duniter node, but can also be a pod)
      * @return
      */
-    public List<EndpointApi> getPeeringTargetedApis() {
+    public Collection<EndpointApi> getPeeringTargetedApis() {
         String[] targetedApis = settings.getAsArray("duniter.p2p.peering.targetedApis", new String[]{
                 EndpointApi.BASIC_MERKLED_API.name(),
                 EndpointApi.BMAS.name()
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/dao/impl/PeerDaoImpl.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/dao/impl/PeerDaoImpl.java
index bc9c77611f2b41da14a39432d9f56eab11be8bb7..9b7ede0d13dec9a014d4ff0ae102ae8086e37801 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/dao/impl/PeerDaoImpl.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/dao/impl/PeerDaoImpl.java
@@ -23,6 +23,7 @@ package org.duniter.elasticsearch.dao.impl;
  */
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.collect.ImmutableList;
 import org.duniter.core.client.model.bma.EndpointApi;
 import org.duniter.core.client.model.local.Peer;
 import org.duniter.core.exception.TechnicalException;
@@ -143,7 +144,8 @@ public class PeerDaoImpl extends AbstractDao implements PeerDao {
 
     @Override
     public List<Peer> getPeersByCurrencyId(String currencyId) {
-        throw new TechnicalException("no implemented: loading all peers may be unsafe for memory...");
+        logger.warn("Calling method PeerSevice.getPeersByCurrencyId() may be unsafe, as it load all peers in memory. Applying workaround: return peer define in config.");
+        return ImmutableList.of(pluginSettings.checkAndGetPeer());
     }
 
     @Override
@@ -372,6 +374,11 @@ public class PeerDaoImpl extends AbstractDao implements PeerDao {
                     //.field("dynamic", "false")
                     .startObject("properties")
 
+                        // stats.software
+                        .startObject(Peer.Stats.PROPERTY_SOFTWARE)
+                        .field("type", "string")
+                        .endObject()
+
                         // stats.version
                         .startObject(Peer.Stats.PROPERTY_VERSION)
                         .field("type", "string")
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/RestModule.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/RestModule.java
index 0a872252d3cd6c6c5933b539b462124037e02c08..193fe0b318edb05fe6b07ccfab08cdb539e76b3a 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/RestModule.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/RestModule.java
@@ -23,6 +23,8 @@ package org.duniter.elasticsearch.rest;
  */
 
 import org.duniter.elasticsearch.rest.attachment.RestImageAttachmentAction;
+import org.duniter.elasticsearch.rest.blockchain.RestBlockchainBlockGetAction;
+import org.duniter.elasticsearch.rest.blockchain.RestBlockchainParametersGetAction;
 import org.duniter.elasticsearch.rest.network.RestNetworkPeeringGetAction;
 import org.duniter.elasticsearch.rest.network.RestNetworkPeeringPostAction;
 import org.duniter.elasticsearch.rest.node.RestNodeSummaryGetAction;
@@ -56,6 +58,9 @@ public class RestModule extends AbstractModule implements Module {
         bind(RestNetworkPeeringGetAction.class).asEagerSingleton();
         bind(RestNetworkPeeringPostAction.class).asEagerSingleton();
 
+        // Blockchain
+        bind(RestBlockchainParametersGetAction.class).asEagerSingleton();
+        bind(RestBlockchainBlockGetAction.class).asEagerSingleton();
 
     }
 }
\ No newline at end of file
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/blockchain/RestBlockchainBlockGetAction.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/blockchain/RestBlockchainBlockGetAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8c4c434f2eef30b9334d0baf41ea2446c87a3ab
--- /dev/null
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/blockchain/RestBlockchainBlockGetAction.java
@@ -0,0 +1,103 @@
+package org.duniter.elasticsearch.rest.blockchain;
+
+/*
+ * #%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 com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.http.entity.ContentType;
+import org.duniter.core.client.config.Configuration;
+import org.duniter.core.client.model.bma.BlockchainBlock;
+import org.duniter.core.client.model.bma.jackson.JacksonUtils;
+import org.duniter.core.exception.TechnicalException;
+import org.duniter.core.util.StringUtils;
+import org.duniter.elasticsearch.PluginSettings;
+import org.duniter.elasticsearch.rest.AbstractRestPostIndexAction;
+import org.duniter.elasticsearch.rest.XContentRestResponse;
+import org.duniter.elasticsearch.rest.security.RestSecurityController;
+import org.duniter.elasticsearch.service.BlockchainService;
+import org.duniter.elasticsearch.service.CurrencyService;
+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.rest.*;
+
+import java.io.IOException;
+
+/**
+ * A rest to post a request to process a new currency/peer.
+ *
+ */
+public class RestBlockchainBlockGetAction extends BaseRestHandler {
+
+    private BlockchainService blockchainService;
+
+    @Inject
+    public RestBlockchainBlockGetAction(Settings settings, RestController controller, Client client, RestSecurityController securityController,
+                                        BlockchainService blockchainService) {
+        super(settings, controller, client);
+
+        securityController.allow(RestRequest.Method.GET, "(/[^/]+)?/blockchain/block/[0-9]+");
+        securityController.allow(RestRequest.Method.GET, "(/[^/]+)?/blockchain/current");
+
+        controller.registerHandler(RestRequest.Method.GET, "/blockchain/block/{number}", this);
+        controller.registerHandler(RestRequest.Method.GET, "/blockchain/current", this);
+        controller.registerHandler(RestRequest.Method.GET, "/{index}/blockchain/block/{number}", this);
+        controller.registerHandler(RestRequest.Method.GET, "/{index}/blockchain/current", this);
+
+        this.blockchainService = blockchainService;
+    }
+
+    @Override
+    protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
+        String currency = request.param("index");
+        int number = request.paramAsInt("number", -1);
+        boolean isCurrent = (number == -1);
+
+        BlockchainBlock block;
+        if (isCurrent) {
+            block = blockchainService.getCurrentBlock(currency);
+        }
+        else {
+            block = blockchainService.getBlockById(currency, number);
+        }
+
+        try {
+            channel.sendResponse(new BytesRestResponse(RestStatus.OK,
+                    ContentType.APPLICATION_JSON.toString(),
+                    getObjectMapper()
+                            .writerWithDefaultPrettyPrinter()
+                            .writeValueAsString(block)));
+        }
+        catch(IOException ioe) {
+            if (isCurrent)
+                throw new TechnicalException(String.format("Error while generating JSON for [/blockchain/current]: %s", ioe.getMessage()), ioe);
+            else
+                throw new TechnicalException(String.format("Error while generating JSON for [/blockchain/block/%s]: %s", number, ioe.getMessage()), ioe);
+        }
+    }
+
+    protected ObjectMapper getObjectMapper() {
+        return JacksonUtils.getThreadObjectMapper();
+    }
+}
\ No newline at end of file
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/blockchain/RestBlockchainParametersGetAction.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/blockchain/RestBlockchainParametersGetAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e5bb15f8b8701ffc9b69588c259cd19185ea9fa
--- /dev/null
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/blockchain/RestBlockchainParametersGetAction.java
@@ -0,0 +1,82 @@
+package org.duniter.elasticsearch.rest.blockchain;
+
+/*
+ * #%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 com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.http.entity.ContentType;
+import org.duniter.core.client.model.bma.BlockchainBlock;
+import org.duniter.core.client.model.bma.BlockchainParameters;
+import org.duniter.core.client.model.bma.jackson.JacksonUtils;
+import org.duniter.core.exception.TechnicalException;
+import org.duniter.elasticsearch.rest.security.RestSecurityController;
+import org.duniter.elasticsearch.service.BlockchainService;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.rest.*;
+
+import java.io.IOException;
+
+/**
+ * A rest to post a request to process a new currency/peer.
+ *
+ */
+public class RestBlockchainParametersGetAction extends BaseRestHandler {
+
+    private BlockchainService blockchainService;
+
+    @Inject
+    public RestBlockchainParametersGetAction(Settings settings, RestController controller, Client client, RestSecurityController securityController,
+                                             BlockchainService blockchainService) {
+        super(settings, controller, client);
+
+        securityController.allow(RestRequest.Method.GET, "(/[^/]+)?/blockchain/parameters");
+
+        controller.registerHandler(RestRequest.Method.GET, "/blockchain/parameters", this);
+        controller.registerHandler(RestRequest.Method.GET, "/{index}/blockchain/parameters", this);
+
+        this.blockchainService = blockchainService;
+    }
+
+    @Override
+    protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
+
+        String currency = request.param("index");
+        BlockchainParameters parameters = blockchainService.getParameters(currency);
+
+        try {
+            channel.sendResponse(new BytesRestResponse(RestStatus.OK,
+                    ContentType.APPLICATION_JSON.toString(),
+                    getObjectMapper()
+                            .writerWithDefaultPrettyPrinter()
+                            .writeValueAsString(parameters)));
+        }
+        catch(IOException ioe) {
+            throw new TechnicalException(String.format("Error while generating JSON for [/blockchain/parameters]: %s", ioe.getMessage()), ioe);
+        }
+    }
+
+    protected ObjectMapper getObjectMapper() {
+        return JacksonUtils.getThreadObjectMapper();
+    }
+}
\ No newline at end of file
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/network/RestNetworkPeeringPostAction.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/network/RestNetworkPeeringPostAction.java
index 16e3719e95d533c712abea74d6d5440c2d1aa4b1..8d9d6d28a03df879feff4f20bf6c7bbbe9f57782 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/network/RestNetworkPeeringPostAction.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/rest/network/RestNetworkPeeringPostAction.java
@@ -89,7 +89,7 @@ public class RestNetworkPeeringPostAction extends BaseRestHandler {
 
             String peerDocument = content.getProperty("peer");
             if (StringUtils.isBlank(peerDocument)) {
-                throw new TechnicalException("Inavlid request: 'peer' property not found");
+                throw new TechnicalException("Invalid request: 'peer' property not found");
             }
 
             // Decode content
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java
index a0dc8c6c2cf3b0f539af5306f294f06bd8cf5a29..ec22a81156ef613951aa5f9077face5b7f4b586b 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/BlockchainService.java
@@ -26,6 +26,7 @@ package org.duniter.elasticsearch.service;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import org.duniter.core.client.dao.CurrencyDao;
 import org.duniter.core.client.model.bma.BlockchainBlock;
 import org.duniter.core.client.model.bma.BlockchainParameters;
 import org.duniter.core.client.model.bma.EndpointApi;
@@ -33,6 +34,8 @@ import org.duniter.core.client.model.local.Peer;
 import org.duniter.core.client.service.bma.BlockchainRemoteService;
 import org.duniter.core.client.service.bma.NetworkRemoteService;
 import org.duniter.core.client.service.exception.BlockNotFoundException;
+import org.duniter.core.client.util.KnownBlocks;
+import org.duniter.core.client.util.KnownCurrencies;
 import org.duniter.core.exception.TechnicalException;
 import org.duniter.core.model.NullProgressionModel;
 import org.duniter.core.model.ProgressionModel;
@@ -41,22 +44,20 @@ import org.duniter.core.util.CollectionUtils;
 import org.duniter.core.util.ObjectUtils;
 import org.duniter.core.util.Preconditions;
 import org.duniter.core.util.StringUtils;
+import org.duniter.core.util.cache.Cache;
+import org.duniter.core.util.cache.SimpleCache;
 import org.duniter.core.util.json.JsonAttributeParser;
 import org.duniter.core.util.websocket.WebsocketClientEndpoint;
 import org.duniter.elasticsearch.PluginSettings;
 import org.duniter.elasticsearch.client.Duniter4jClient;
 import org.duniter.elasticsearch.dao.BlockDao;
+import org.duniter.elasticsearch.dao.CurrencyExtendDao;
 import org.duniter.elasticsearch.exception.DuplicateIndexIdException;
 import org.duniter.elasticsearch.threadpool.ThreadPool;
-import org.elasticsearch.action.admin.cluster.node.info.NodesInfoAction;
-import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequestBuilder;
-import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
-import org.elasticsearch.action.admin.cluster.node.info.TransportNodesInfoAction;
 import org.elasticsearch.action.bulk.BulkItemResponse;
 import org.elasticsearch.action.bulk.BulkRequestBuilder;
 import org.elasticsearch.action.bulk.BulkResponse;
 import org.elasticsearch.common.inject.Inject;
-import org.elasticsearch.rest.action.admin.cluster.node.info.RestNodesInfoAction;
 import org.nuiton.i18n.I18n;
 
 import java.io.IOException;
@@ -67,6 +68,7 @@ import java.util.*;
  */
 public class BlockchainService extends AbstractService {
 
+    private static final BlockchainBlock DEFAULT_BLOCK = KnownBlocks.getFirstBlock(KnownCurrencies.G1);
     public static final String BLOCK_TYPE = BlockDao.TYPE;
     public static final String CURRENT_BLOCK_ID = "current";
 
@@ -83,17 +85,21 @@ public class BlockchainService extends AbstractService {
     private final JsonAttributeParser<String> blockHashParser = new JsonAttributeParser<>("hash", String.class);
     private final JsonAttributeParser<String> blockPreviousHashParser = new JsonAttributeParser<>("previousHash", String.class);
 
+    private SimpleCache<String, BlockchainParameters> blockchainParametersCurrencyIdCache;
     private BlockDao blockDao;
+    private CurrencyExtendDao currencyDao;
 
     @Inject
     public BlockchainService(Duniter4jClient client,
                              PluginSettings settings,
                              ThreadPool threadPool,
                              BlockDao blockDao,
+                             CurrencyDao currencyDao,
                              final ServiceLocator serviceLocator){
         super("duniter.blockchain", client, settings);
         this.client = client;
         this.blockDao = blockDao;
+        this.currencyDao = (CurrencyExtendDao) currencyDao;
         threadPool.scheduleOnStarted(() -> {
             blockchainRemoteService = serviceLocator.getBlockchainRemoteService();
             setIsReady(true);
@@ -112,6 +118,13 @@ public class BlockchainService extends AbstractService {
                 }
             }
         };
+        blockchainParametersCurrencyIdCache = new SimpleCache<String, BlockchainParameters>(/*eternal*/) {
+            @Override
+            public BlockchainParameters load(String currencyId) {
+                if (!isReady()) throw new IllegalStateException("Could not load blockchain parameters (service is not started)");
+                return blockchainRemoteService.getParameters(currencyId);
+            }
+        };
     }
 
 
@@ -466,12 +479,35 @@ public class BlockchainService extends AbstractService {
         }
     }
 
-    public BlockchainBlock getBlockById(final String currencyName, final int number) {
-        return blockDao.getBlockById(currencyName, String.valueOf(number));
+    public BlockchainBlock getBlockById(String currency, final int number) {
+
+        // Retrieve the currency to use
+        boolean enableBlockchainIndexation = pluginSettings.enableBlockchainIndexation() && currencyDao.existsIndex();
+        if (StringUtils.isBlank(currency)) {
+            List<String> currencyIds = enableBlockchainIndexation ? currencyDao.getCurrencyIds() : null;
+            if (CollectionUtils.isNotEmpty(currencyIds)) {
+                currency = currencyIds.get(0);
+            } else {
+                currency = DEFAULT_BLOCK.getCurrency();
+            }
+        }
+
+        return blockDao.getBlockById(currency, String.valueOf(number));
     }
 
-    public BlockchainBlock getCurrentBlock(final String currencyName) {
-        return blockDao.getBlockById(currencyName, CURRENT_BLOCK_ID);
+    public BlockchainBlock getCurrentBlock(String currency) {
+        // Retrieve the currency to use
+        boolean enableBlockchainIndexation = pluginSettings.enableBlockchainIndexation() && currencyDao.existsIndex();
+        if (StringUtils.isBlank(currency)) {
+            List<String> currencyIds = enableBlockchainIndexation ? currencyDao.getCurrencyIds() : null;
+            if (CollectionUtils.isNotEmpty(currencyIds)) {
+                currency = currencyIds.get(0);
+            } else {
+                currency = DEFAULT_BLOCK.getCurrency();
+            }
+        }
+
+        return blockDao.getBlockById(currency, CURRENT_BLOCK_ID);
     }
 
     public void deleteFrom(final String currencyName, final int fromBlock) {
@@ -539,6 +575,26 @@ public class BlockchainService extends AbstractService {
         return missingBlockNumbers;
     }
 
+    public BlockchainParameters getParameters(String currency) {
+        // Check in cache
+        if (StringUtils.isNotBlank(currency)) {
+            return blockchainParametersCurrencyIdCache.get(currency);
+        }
+
+        // Or get default
+        BlockchainParameters result = blockchainParametersCurrencyIdCache.getIfPresent("DEFAULT");
+
+        // Or fill default using duniter node
+        if (result == null && pluginSettings.enableBlockchainIndexation()) {
+            Peer peer = pluginSettings.checkAndGetPeer();
+            result = blockchainRemoteService.getParameters(peer);
+            blockchainParametersCurrencyIdCache.put(result.getCurrency(), result);
+            blockchainParametersCurrencyIdCache.put("DEFAULT", result);
+        }
+
+        return result;
+    }
+
     private Collection<String> indexBlocksUsingBulk(Peer peer, String currencyName, int firstNumber, int lastNumber, ProgressionModel progressionModel,
                                                     boolean isLastCurrentNumber) {
         Set<String> missingBlockNumbers = new LinkedHashSet<>();
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java
index 0323203a874d6fc4fd0e492095c0d9cf4e2a7324..a7c3b7506ba700a2ec113e2bb5f128ecfb9f73e1 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/CurrencyService.java
@@ -214,8 +214,8 @@ public class CurrencyService extends AbstractService {
                     createIndexRequestBuilder.addMapping(blockDao.getType(), blockDao.createTypeMapping());
 
                     // Add movement type
-                    MovementDao operationDao = ServiceLocator.instance().getBean(MovementDao.class);
-                    createIndexRequestBuilder.addMapping(operationDao.getType(), operationDao.createTypeMapping());
+                    MovementDao movementDao = ServiceLocator.instance().getBean(MovementDao.class);
+                    createIndexRequestBuilder.addMapping(movementDao.getType(), movementDao.createTypeMapping());
 
                     // Add blockStat type
                     BlockStatDao blockStatDao = injector.getInstance(BlockStatDao.class);
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/NetworkService.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/NetworkService.java
index ebf295c64cbe94537e5739f2e684554a9085fa15..45c444a53907122e1e8063d17d7da8ca30672c22 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/NetworkService.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/NetworkService.java
@@ -49,6 +49,7 @@ import org.elasticsearch.common.inject.Inject;
 
 import java.io.IOException;
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -73,6 +74,7 @@ public class NetworkService extends AbstractService {
     private HttpService httpService;
     private NetworkRemoteService networkRemoteService;
     private PeerService peerService;
+    private final boolean debug;
 
     @Inject
     public NetworkService(Duniter4jClient client,
@@ -91,6 +93,7 @@ public class NetworkService extends AbstractService {
         this.blockchainService = blockchainService;
         this.peerService = peerService;
         this.threadPool = threadPool;
+        this.debug = logger.isDebugEnabled();
         threadPool.scheduleOnStarted(() -> {
             this.httpService = serviceLocator.getHttpService();
             this.networkRemoteService = serviceLocator.getNetworkRemoteService();
@@ -142,7 +145,7 @@ public class NetworkService extends AbstractService {
                 }
 
             } catch (IOException e) {
-                if (logger.isDebugEnabled()) {
+                if (debug) {
                     logger.warn(String.format("Unable to parse P2P endpoint [%s]: %s", endpoint, e.getMessage()), e);
                 }
                 else {
@@ -400,7 +403,7 @@ public class NetworkService extends AbstractService {
             peering = NetworkPeerings.parse(peeringDocument);
         }
         catch(Exception e) {
-            throw new TechnicalException("Inavlid peer document: " + e.getMessage(), e);
+            throw new TechnicalException("Invalid peer document: " + e.getMessage(), e);
         }
 
         // Check validity then save
@@ -424,22 +427,26 @@ public class NetworkService extends AbstractService {
             Peer peer = Peer.newBuilder()
                     .setCurrency(peering.getCurrency())
                     .setPubkey(peering.getPubkey())
-                    .setEndpoint(ep).build();
-            EndpointApi api = EndpointApi.valueOf(peer.getApi());
-            peers.add(peer);
+                    .setEndpoint(ep)
+                    .build();
+
+            Peer.Stats stats = new Peer.Stats();
+            peer.setStats(stats);
 
-            // TODO: filter to keep only useful API ?
-            //if (targetPeersEndpointApis.contains(api)) {
-            //    peers.add(peer);
-            //}
-            //else {
-            //    logger.debug(String.format("Ignoring endpoint {%s}: not a targeted API", peer));
-            //}
+            String blockStamp = peering.getBlock();
+            if (StringUtils.isNotBlank(blockStamp)) {
+                String[] blockParts = blockStamp.split("-");
+                stats.setBlockNumber(Integer.parseInt(blockParts[0]));
+                stats.setBlockHash(blockParts[1]);
+            }
+
+            peers.add(peer);
         }
 
-        // Save peers
-        if (CollectionUtils.isEmpty(peers)) {
-            peerService.save(peering.getCurrency(), peers, false);
+        // Save peers (if not empty)
+        if (CollectionUtils.isNotEmpty(peers)) {
+            if (debug) logger.debug("Saving peers: " + peers.toString());
+            peerService.save(peering.getCurrency(), peers, false, true, true);
         }
 
         return peering;
@@ -528,12 +535,12 @@ public class NetworkService extends AbstractService {
 
     }
 
-    protected void addAllTargetPeerEndpointApis(List<EndpointApi> apis) {
+    protected void addAllTargetPeerEndpointApis(Collection<EndpointApi> apis) {
         Preconditions.checkNotNull(apis);
         apis.forEach(this::addTargetPeerEndpointApi);
     }
 
-    protected void addAllPublishEndpointApis(List<EndpointApi> apis) {
+    protected void addAllPublishEndpointApis(Collection<EndpointApi> apis) {
         Preconditions.checkNotNull(apis);
         apis.forEach(this::addPublishEndpointApi);
     }
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/PeerService.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/PeerService.java
index 166d411737612ba1fdd689003207d0a2ec9f12fa..234acae391a03ba27bf91d951d0bcd61ba71fc17 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/PeerService.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/service/PeerService.java
@@ -24,7 +24,7 @@ package org.duniter.elasticsearch.service;
 
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import org.duniter.core.client.dao.PeerDao;
 import org.duniter.core.client.model.bma.BlockchainParameters;
 import org.duniter.core.client.model.bma.EndpointApi;
@@ -39,7 +39,10 @@ import org.duniter.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.common.inject.Inject;
 import org.nuiton.i18n.I18n;
 
+import java.util.Collection;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Created by Benoit on 30/03/2015.
@@ -53,10 +56,8 @@ public class PeerService extends AbstractService  {
     private ThreadPool threadPool;
 
     // Define endpoint API to include
-    private static List<String> includeEndpointApis = Lists.newArrayList(
-            EndpointApi.BASIC_MERKLED_API.name(),
-            EndpointApi.BMAS.name(),
-            EndpointApi.WS2P.name());
+    // API to include inside when getting peers
+    private final static Set<String> indexedEndpointApis = Sets.newHashSet();
 
     @Inject
     public PeerService(Duniter4jClient client, PluginSettings settings, ThreadPool threadPool,
@@ -71,22 +72,29 @@ public class PeerService extends AbstractService  {
             this.delegate = serviceLocator.getPeerService();
             setIsReady(true);
         });
+
+        // If filtered API defined in settings, use it
+        if (CollectionUtils.isNotEmpty(pluginSettings.getPeerIndexedApis())) {
+            addAllPeerIndexedEndpointApis(pluginSettings.getPeerIndexedApis());
+        }
     }
 
-    public PeerService addIncludeEndpointApi(String api) {
+    public PeerService addIndexedEndpointApi(String api) {
         Preconditions.checkNotNull(api);
-        if (!includeEndpointApis.contains(api)) {
-            includeEndpointApis.add(api);
-        }
+        if (!indexedEndpointApis.contains(api)) indexedEndpointApis.add(api);
         return this;
     }
 
-    public PeerService addIncludeEndpointApi(EndpointApi api) {
+    public PeerService addIndexedEndpointApi(EndpointApi api) {
         Preconditions.checkNotNull(api);
-        addIncludeEndpointApi(api.name());
+        addIndexedEndpointApi(api.name());
         return this;
     }
 
+    public void setCurrencyMainPeer(String currency, Peer peer) {
+        delegate.setCurrencyMainPeer(currency, peer);
+    }
+
     public PeerService indexPeers(Peer peer) {
 
         try {
@@ -117,7 +125,7 @@ public class PeerService extends AbstractService  {
             org.duniter.core.client.service.local.NetworkService.Filter filterDef = new org.duniter.core.client.service.local.NetworkService.Filter();
             filterDef.filterType = null;
             filterDef.filterStatus = Peer.PeerStatus.UP;
-            filterDef.filterEndpoints = ImmutableList.copyOf(includeEndpointApis);
+            filterDef.filterEndpoints = ImmutableList.copyOf(indexedEndpointApis);
 
             // Default sort
             org.duniter.core.client.service.local.NetworkService.Sort sortDef = new org.duniter.core.client.service.local.NetworkService.Sort();
@@ -137,7 +145,31 @@ public class PeerService extends AbstractService  {
         delegate.save(peer);
     }
 
-    public void save(final String currencyId, final List<Peer> peers, boolean isFullList) {
+    public void save(final String currencyId, final List<Peer> peers, boolean isFullList, boolean applyFilterEndpoints, boolean refreshPeers) {
+        // Skip if empty and NOT the full list
+        if (!isFullList && CollectionUtils.isEmpty(peers)) return;
+
+        // Filter on endpoint apis
+        if (applyFilterEndpoints) {
+            List<Peer> filteredPeers = peers.stream().filter(p -> indexedEndpointApis.contains(p.getApi())).collect(Collectors.toList());
+            save(currencyId, filteredPeers, isFullList, false, refreshPeers); // Loop, without applying filter
+            return;
+        }
+
+        if (refreshPeers) {
+            final Peer mainPeer = pluginSettings.checkAndGetPeer();
+
+            // Async refresh
+            networkService.asyncRefreshPeers(mainPeer, peers, threadPool.scheduler())
+                    .exceptionally(throwable -> {
+                        logger.error("Could not refresh peers status: " + throwable.getMessage(), throwable);
+                        return peers;
+                    })
+                    // then loop, without refreshing
+                    .thenAccept(list -> save(currencyId, list, isFullList, false, false));
+            return;
+        }
+
         delegate.save(currencyId, peers, isFullList);
     }
 
@@ -154,7 +186,7 @@ public class PeerService extends AbstractService  {
         NetworkService.Filter filterDef = new NetworkService.Filter();
         filterDef.filterType = null;
         filterDef.filterStatus = Peer.PeerStatus.UP;
-        filterDef.filterEndpoints = ImmutableList.copyOf(includeEndpointApis);
+        filterDef.filterEndpoints = ImmutableList.copyOf(indexedEndpointApis);
         filterDef.currency = currencyName;
 
         // Default sort
@@ -169,4 +201,9 @@ public class PeerService extends AbstractService  {
     public Long getMaxLastUpTime(String currencyId) {
         return peerDao.getMaxLastUpTime(currencyId);
     }
+
+    protected void addAllPeerIndexedEndpointApis(Collection<EndpointApi> apis) {
+        Preconditions.checkNotNull(apis);
+        apis.forEach(this::addIndexedEndpointApi);
+    }
 }
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java
index 06689ebafa78951bde7f7be57a0cc5849633acf8..f83a140fb5ecc56b52c4cff6eefd37dab74e2e6f 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/threadpool/ThreadPool.java
@@ -80,7 +80,7 @@ public class ThreadPool extends AbstractLifecycleComponent<ThreadPool> {
 
     public void doStart(){
         if (logger.isDebugEnabled()) {
-            logger.debug("Starting Duniter4j ThreadPool...");
+            logger.debug("Starting thread pool...");
         }
 
         if (!afterStartedCommands.isEmpty()) {
diff --git a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/util/springtemplate/STUtils.java b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/util/springtemplate/STUtils.java
index cd0cb61b9f40bc0a38a92d411daa633c35253073..a56b5ff4d13bf08fb2cbebbb03820b6bc37e2699 100644
--- a/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/util/springtemplate/STUtils.java
+++ b/cesium-plus-pod-core/src/main/java/org/duniter/elasticsearch/util/springtemplate/STUtils.java
@@ -1,9 +1,7 @@
 package org.duniter.elasticsearch.util.springtemplate;
 
-import org.stringtemplate.v4.DateRenderer;
 import org.stringtemplate.v4.STGroup;
 import org.stringtemplate.v4.STGroupDir;
-import org.stringtemplate.v4.StringRenderer;
 
 import java.util.Date;
 
diff --git a/cesium-plus-pod-subscription/pom.xml b/cesium-plus-pod-subscription/pom.xml
index 266d6f8d64dcdff8e92fa14f2975276e3779ac5f..173963cf23aae95aeef6b7a2411e0c32933b6c20 100644
--- a/cesium-plus-pod-subscription/pom.xml
+++ b/cesium-plus-pod-subscription/pom.xml
@@ -44,7 +44,7 @@
 
     <dependency>
       <groupId>org.antlr</groupId>
-      <artifactId>stringtemplate</artifactId>
+      <artifactId>ST4</artifactId>
       <version>${stringtemplate.version}</version>
       <scope>provided</scope>
     </dependency>
diff --git a/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/PluginSettings.java b/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/PluginSettings.java
index 4a2aba7308ab674b0fddea9343a9f31c47663601..e6ba1ee8190fb40ee8452eb0c7ce92e1f8fe957e 100644
--- a/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/PluginSettings.java
+++ b/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/PluginSettings.java
@@ -28,6 +28,7 @@ import org.duniter.core.util.crypto.KeyPair;
 import org.elasticsearch.common.component.*;
 import org.elasticsearch.common.inject.Inject;
 
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -146,11 +147,11 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
         return delegate.enablePeering();
     }
 
-    public List<EndpointApi> getPeeringTargetedApis() {
+    public Collection<EndpointApi> getPeeringTargetedApis() {
         return this.delegate.getPeeringTargetedApis();
     }
 
-    public List<EndpointApi> getPeeringPublishedApis() {
+    public Collection<EndpointApi> getPeeringPublishedApis() {
         return this.delegate.getPeeringPublishedApis();
     }
 
diff --git a/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/service/SubscriptionService.java b/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/service/SubscriptionService.java
index 45d1048674d233446dce2c1e2a87da9adc2dac11..55c730927f4e834e1b16e7ab45eff85e14209315 100644
--- a/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/service/SubscriptionService.java
+++ b/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/service/SubscriptionService.java
@@ -27,7 +27,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.ImmutableSet;
 import org.duniter.core.client.model.ModelUtils;
-import org.duniter.core.client.model.bma.jackson.JacksonUtils;
 import org.duniter.core.client.model.elasticsearch.Record;
 import org.duniter.core.exception.TechnicalException;
 import org.duniter.core.service.CryptoService;
@@ -43,8 +42,6 @@ import org.duniter.elasticsearch.subscription.dao.record.SubscriptionRecordDao;
 import org.duniter.elasticsearch.subscription.model.SubscriptionExecution;
 import org.duniter.elasticsearch.subscription.model.SubscriptionRecord;
 import org.duniter.elasticsearch.subscription.model.email.EmailSubscription;
-import org.duniter.elasticsearch.subscription.util.stringtemplate.DateRenderer;
-import org.duniter.elasticsearch.subscription.util.stringtemplate.StringRenderer;
 import org.duniter.elasticsearch.threadpool.ThreadPool;
 import org.duniter.elasticsearch.user.model.UserEvent;
 import org.duniter.elasticsearch.user.service.AdminService;
@@ -57,7 +54,6 @@ import org.elasticsearch.common.unit.TimeValue;
 import org.nuiton.i18n.I18n;
 import org.stringtemplate.v4.ST;
 import org.stringtemplate.v4.STGroup;
-import org.stringtemplate.v4.STGroupDir;
 
 import java.text.SimpleDateFormat;
 import java.util.*;
@@ -78,6 +74,7 @@ public class SubscriptionService extends AbstractService {
     private UserService userService;
     private String emailSubjectPrefix;
     private STGroup templates;
+    private boolean debug;
 
     @Inject
     public SubscriptionService(Duniter4jClient client,
@@ -102,8 +99,10 @@ public class SubscriptionService extends AbstractService {
         if (StringUtils.isNotBlank(emailSubjectPrefix)) {
             emailSubjectPrefix += " "; // add one trailing space
         }
+        this.debug = logger.isDebugEnabled();
 
         // Configure springtemplate engine
+        STGroup.verbose = debug;
         templates = STUtils.newSTGroup("org/duniter/elasticsearch/subscription/templates");
         Preconditions.checkNotNull(templates.getInstanceOf("text_email"), "Missing ST template {text_email}");
         Preconditions.checkNotNull(templates.getInstanceOf("html_email_content"), "Missing ST template {html_email_content}");
@@ -269,19 +268,19 @@ public class SubscriptionService extends AbstractService {
         boolean debug = pluginSettings.isEmailSubscriptionsDebug();
         if (subscription.getContent() != null && subscription.getContent().getEmail() != null) {
             if (debug) {
-                logger.info(String.format("Processing email subscription to [%s - %s] on account [%s]",
+                logger.info(String.format("Processing email subscription from {%s} to {%s} (pubkey: %s)",
                         senderName,
                         subscription.getContent().getEmail(),
                         ModelUtils.minifyPubkey(subscription.getIssuer())));
             }
             else {
-                logger.info(String.format("Processing email subscription [%s] on account [%s]",
+                logger.info(String.format("Processing email subscription to {%s} (pubkey: %s)",
                         subscription.getId(),
                         ModelUtils.minifyPubkey(subscription.getIssuer())));
             }
         }
         else {
-            logger.warn(String.format("Processing email subscription [%s] - no email found in subscription content: skipping", subscription.getId()));
+            logger.warn(String.format("Processing email subscription {id=%s}: no email found in content. Skipping", subscription.getId()));
             return null;
         }
 
@@ -313,9 +312,7 @@ public class SubscriptionService extends AbstractService {
         // Get user locale
         String[] localParts = subscription.getContent() != null && subscription.getContent().getLocale() != null ?
                 subscription.getContent().getLocale().split("-") : new String[]{"en", "GB"};
-        Locale issuerLocale = localParts.length >= 2 ? new Locale(localParts[0].toLowerCase(), localParts[1].toUpperCase()) : new Locale(localParts[0].toLowerCase());
-
-
+        Locale userLocale = localParts.length >= 2 ? new Locale(localParts[0].toLowerCase(), localParts[1].toUpperCase()) : new Locale(localParts[0].toLowerCase());
 
         // Compute text content
         final String text = fillTemplate(
@@ -325,8 +322,9 @@ public class SubscriptionService extends AbstractService {
                 senderName,
                 profileTitles,
                 userEvents,
+                userLocale,
                 pluginSettings.getEmailLinkUrl())
-                .render(issuerLocale);
+                .render(userLocale);
 
         // Compute HTML content
         final String html = fillTemplate(
@@ -336,8 +334,9 @@ public class SubscriptionService extends AbstractService {
                 senderName,
                 profileTitles,
                 userEvents,
+                userLocale,
                 pluginSettings.getEmailLinkUrl())
-                .render(issuerLocale);
+                .render(userLocale);
 
         final String object = emailSubjectPrefix + I18n.t("duniter4j.es.subscription.email.subject", userEvents.size());
         if (pluginSettings.isEmailSubscriptionsDebug()) {
@@ -370,12 +369,13 @@ public class SubscriptionService extends AbstractService {
     }
 
 
-    public static ST fillTemplate(ST template,
+    public static ST fillTemplate(final ST template,
                                   EmailSubscription subscription,
                                   String senderPubkey,
                                   String senderName,
                                   Map<String, String> issuerProfilNames,
                                   List<UserEvent> userEvents,
+                                  final Locale issuerLocale,
                                   String cesiumSiteUrl) {
         String issuerName = issuerProfilNames != null && issuerProfilNames.containsKey(subscription.getIssuer()) ?
                 issuerProfilNames.get(subscription.getIssuer()) :
@@ -393,11 +393,9 @@ public class SubscriptionService extends AbstractService {
             template.add("senderPubkey", senderPubkey);
             template.add("senderName", senderName);
             userEvents.forEach(userEvent -> {
-                String description = userEvent.getParams() != null ?
-                        I18n.t("duniter.user.event." + userEvent.getCode().toUpperCase(), userEvent.getParams()) :
-                        I18n.t("duniter.user.event." + userEvent.getCode().toUpperCase());
+                String description = getUserEventDescription(issuerLocale, userEvent);
                 template.addAggr("events.{description, time}", new Object[]{
-                    description,
+                        description,
                     new Date(userEvent.getTime() * 1000)
                 });
             });
@@ -451,5 +449,42 @@ public class SubscriptionService extends AbstractService {
         }
     }
 
+    private static String getUserEventDescription(Locale locale, UserEvent userEvent) {
+        final String defaultKey = "duniter.user.event." + userEvent.getCode().toUpperCase();
+
+        // Retrieve the translated description: prefer 'duniter.<INDEX>.event.<CODE>' if exists,
+        // and 'duniter.user.event.<CODE>' otherwise
+        final UserEvent.Reference reference = userEvent.getReference();
+        if (reference != null && reference.getIndex() != null) {
+            return firstValidI18n(locale,
+                    new String[]{
+                        String.format("duniter.%s.%s.event.%s", userEvent.getReference().getIndex().toLowerCase(), userEvent.getReference().getType().toLowerCase(), userEvent.getCode().toUpperCase()),
+                        String.format("duniter.%s.event.%s", userEvent.getReference().getIndex().toLowerCase(), userEvent.getCode().toUpperCase()),
+                        defaultKey
+                    },
+                    userEvent.getParams());
+        }
+
+        return userEvent.getParams() != null ?
+                I18n.l(locale, defaultKey, userEvent.getParams()) :
+                I18n.l(locale, defaultKey);
+    }
 
+    /**
+     * Return the first valid i18N translation
+     * @param locale
+     * @param keys
+     * @param params
+     * @return
+     */
+    private static String firstValidI18n(Locale locale, String[] keys, String[] params) {
+        String result = null;
+        for (String key: keys) {
+            result = params != null ? I18n.l(locale, key, params) : I18n.l(locale, key);
+            if (!key.equals(result)) {
+                return result;
+            }
+        }
+        return result;
+    }
 }
diff --git a/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/util/stringtemplate/DateRenderer.java b/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/util/stringtemplate/DateRenderer.java
deleted file mode 100644
index 9fb695d17a0636af3a21bfc5d6fc1bb43bca2ad9..0000000000000000000000000000000000000000
--- a/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/util/stringtemplate/DateRenderer.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.duniter.elasticsearch.subscription.util.stringtemplate;
-
-/*-
- * #%L
- * Duniter4j :: ElasticSearch Subscription plugin
- * %%
- * Copyright (C) 2014 - 2017 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.stringtemplate.v4.AttributeRenderer;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.*;
-
-public class DateRenderer implements AttributeRenderer {
-
-    public DateRenderer() {
-    }
-
-    public String toString(Object o, String formatString, Locale locale) {
-        if(formatString == null) {
-            formatString = "short";
-        }
-
-        Date d;
-        if(o instanceof Calendar) {
-            d = ((Calendar)o).getTime();
-        } else {
-            d = (Date)o;
-        }
-
-        Integer styleI = (Integer)org.stringtemplate.v4.DateRenderer.formatToInt.get(formatString);
-        Object f;
-        if(styleI == null) {
-            f = new SimpleDateFormat(formatString, locale);
-        } else {
-            int style = styleI.intValue();
-            if(formatString.startsWith("date:")) {
-                f = DateFormat.getDateInstance(style, locale);
-            } else if(formatString.startsWith("time:")) {
-                f = DateFormat.getTimeInstance(style, locale);
-            } else {
-                f = DateFormat.getDateTimeInstance(style, style, locale);
-            }
-        }
-
-        return ((DateFormat)f).format(d);
-    }
-}
\ No newline at end of file
diff --git a/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/util/stringtemplate/StringRenderer.java b/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/util/stringtemplate/StringRenderer.java
deleted file mode 100644
index fdbda8bdad3ba14d55f3e2bc0dc31f4cb20e4288..0000000000000000000000000000000000000000
--- a/cesium-plus-pod-subscription/src/main/java/org/duniter/elasticsearch/subscription/util/stringtemplate/StringRenderer.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.duniter.elasticsearch.subscription.util.stringtemplate;
-
-/*-
- * #%L
- * Duniter4j :: ElasticSearch Subscription plugin
- * %%
- * Copyright (C) 2014 - 2017 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.duniter.core.client.model.ModelUtils;
-import org.duniter.core.util.CollectionUtils;
-import org.duniter.core.util.StringUtils;
-import org.nuiton.i18n.I18n;
-import org.stringtemplate.v4.AttributeRenderer;
-
-import java.util.Locale;
-
-/**
- * Add format capabilities: i18n, pubkey
- * Created by blavenie on 10/04/17.
- */
-public class StringRenderer extends org.stringtemplate.v4.StringRenderer{
-
-    @Override
-    public String toString(Object o, String formatString, Locale locale) {
-        return formatString == null ? (String)o :
-                (formatString.equals("pubkey") ? ModelUtils.minifyPubkey((String)o) :
-                        (formatString.startsWith("i18n") ? toI18nString(o, formatString, locale) :
-                            super.toString(o, formatString, locale)));
-    }
-
-    protected String toI18nString(Object key, String formatString, Locale locale) {
-        String[] params = formatString.startsWith("i18n:") ? formatString.substring(5).split(",") : null;
-        if (CollectionUtils.isNotEmpty(params)) {
-            return I18n.l(locale, key.toString(), params);
-        }
-        return I18n.l(locale, key.toString());
-    }
-}
diff --git a/cesium-plus-pod-subscription/src/test/java/org/duniter/elasticsearch/subscription/service/SubscriptionTemplateTest.java b/cesium-plus-pod-subscription/src/test/java/org/duniter/elasticsearch/subscription/service/SubscriptionTemplateTest.java
index 33932a39e2e83c0da74097cec9210e2d52d8c15d..94a0689f67be545b696581a5da378b21ac957701 100644
--- a/cesium-plus-pod-subscription/src/test/java/org/duniter/elasticsearch/subscription/service/SubscriptionTemplateTest.java
+++ b/cesium-plus-pod-subscription/src/test/java/org/duniter/elasticsearch/subscription/service/SubscriptionTemplateTest.java
@@ -24,14 +24,12 @@ package org.duniter.elasticsearch.subscription.service;
 
 import org.duniter.core.client.model.ModelUtils;
 import org.duniter.core.exception.TechnicalException;
-import org.duniter.elasticsearch.subscription.util.stringtemplate.DateRenderer;
-import org.duniter.elasticsearch.subscription.util.stringtemplate.StringRenderer;
+import org.duniter.elasticsearch.util.springtemplate.STUtils;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.stringtemplate.v4.ST;
 import org.stringtemplate.v4.STGroup;
-import org.stringtemplate.v4.STGroupDir;
 
 import java.util.Date;
 import java.util.Locale;
@@ -44,16 +42,13 @@ import static org.junit.Assert.assertNotNull;
 public class SubscriptionTemplateTest {
     private static final Logger log = LoggerFactory.getLogger(SubscriptionTemplateTest.class);
 
-    private static final boolean verbose = false;
+    private static final boolean verbose = true;
 
     @Test
     public void testHtmlEmail() throws Exception{
 
         try {
-            STGroup group = new STGroupDir("templates", '$', '$');
-
-            group.registerRenderer(Date.class, new DateRenderer());
-            group.registerRenderer(String.class, new StringRenderer());
+            STGroup group = STUtils.newSTGroup("org/duniter/elasticsearch/subscription/templates");
 
             ST tpl = group.getInstanceOf("html_email_content");
             tpl.add("issuerName", "MyIssuerName");
@@ -80,10 +75,7 @@ public class SubscriptionTemplateTest {
     public void testTextEmail() throws Exception{
 
         try {
-            STGroup group = new STGroupDir("templates", '$', '$');
-
-            group.registerRenderer(Date.class, new DateRenderer());
-            group.registerRenderer(String.class, new StringRenderer());
+            STGroup group = STUtils.newSTGroup("org/duniter/elasticsearch/subscription/templates");
 
             ST tpl = group.getInstanceOf("text_email");
             tpl.add("issuerPubkey", "5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of");
diff --git a/cesium-plus-pod-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java b/cesium-plus-pod-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java
index 823bae9d37101a80e5fa20163d307a3abd116a5c..fdc05f1b3a43a7c579ea4a815212c76dc68d6030 100644
--- a/cesium-plus-pod-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java
+++ b/cesium-plus-pod-user/src/main/java/org/duniter/elasticsearch/user/PluginSettings.java
@@ -29,7 +29,7 @@ import org.elasticsearch.common.component.AbstractLifecycleComponent;
 import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.settings.Settings;
 
-import java.util.List;
+import java.util.Collection;
 import java.util.Locale;
 
 /**
@@ -98,11 +98,11 @@ public class PluginSettings extends AbstractLifecycleComponent<PluginSettings> {
         return this.delegate.enablePeering();
     }
 
-    public List<EndpointApi> getPeeringTargetedApis() {
+    public Collection<EndpointApi> getPeeringTargetedApis() {
         return this.delegate.getPeeringTargetedApis();
     }
 
-    public List<EndpointApi> getPeeringPublishedApis() {
+    public Collection<EndpointApi> getPeeringPublishedApis() {
         return this.delegate.getPeeringPublishedApis();
     }
 
diff --git a/pom.xml b/pom.xml
index ad0d71fada06368eafb7221e2a58bbfb603e51fd..c95d1d8adc3ddce529b648658c5815727fa94580 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
     <signatureVersion>1.0</signatureVersion>
 
     <!-- Commons versions -->
-    <duniter4j.version>1.1.0</duniter4j.version>
+    <duniter4j.version>1.1.1</duniter4j.version>
     <log4j.version>1.2.17</log4j.version>
     <slf4j.version>1.7.6</slf4j.version>
     <guava.version>22.0</guava.version>
@@ -41,7 +41,7 @@
     <jna.version>4.2.0</jna.version><!--4.3.0 could not build -->
     <tyrus.version>1.14</tyrus.version>
     <jackson.version>2.9.5</jackson.version>
-    <stringtemplate.version>4.0.2</stringtemplate.version>
+    <stringtemplate.version>4.1</stringtemplate.version>
     <jTextUtilsVersion>0.3.3</jTextUtilsVersion>
 
     <nuitonConfigVersion>3.0</nuitonConfigVersion>
@@ -166,7 +166,7 @@
       <dependency>
         <groupId>org.duniter</groupId>
         <artifactId>duniter4j-core-client</artifactId>
-        <version>1.1.0</version>
+        <version>${duniter4j.version}</version>
       </dependency>
 
       <dependency>
@@ -196,7 +196,7 @@
       </dependency>
       <dependency>
         <groupId>org.antlr</groupId>
-        <artifactId>stringtemplate</artifactId>
+        <artifactId>ST4</artifactId>
         <version>${stringtemplate.version}</version>
         <exclusions>
           <exclusion>