diff --git a/src/call.cpp b/src/call.cpp
index 5b568d5b9a581d622c7eae640538090c5a210c5a..da9f76528333fd9013cef698ba06430ac8fafdda 100644
--- a/src/call.cpp
+++ b/src/call.cpp
@@ -22,6 +22,7 @@
 
 #include "call.h"
 #include "account.h"
+#include "jamidht/jamiaccount.h"
 #include "manager.h"
 #ifdef ENABLE_PLUGIN
 #include "plugin/jamipluginmanager.h"
@@ -122,6 +123,22 @@ Call::Call(const std::shared_ptr<Account>& account,
                     std::chrono::seconds(timeout));
             }
 
+            if (!isSubcall() && getCallType() == CallType::OUTGOING) {
+                if (cnx_state == ConnectionState::CONNECTED && duration_start_ == time_point::min())
+                    duration_start_ = clock::now();
+                else if (cnx_state == ConnectionState::DISCONNECTED) {
+                    if (auto jamiAccount = std::dynamic_pointer_cast<JamiAccount>(
+                            getAccount().lock())) {
+                        auto duration = duration_start_ == time_point::min()
+                                            ? 0
+                                            : std::chrono::duration_cast<std::chrono::milliseconds>(
+                                                  clock::now() - duration_start_)
+                                                  .count();
+                        jamiAccount->addCallHistoryMessage(getPeerNumber(), duration);
+                    }
+                }
+            }
+
             // kill pending subcalls at disconnect
             if (call_state == CallState::OVER)
                 hangupCalls(safePopSubcalls(), 0);
diff --git a/src/call.h b/src/call.h
index 43147dfd8b84f3540dfefa69426b8cb27afbb9a3..24a86e270da7e5990d0195e3b19c8aceb64d4107 100644
--- a/src/call.h
+++ b/src/call.h
@@ -396,6 +396,9 @@ protected:
     mutable ConfInfo confInfo_ {};
 
 private:
+    using clock = std::chrono::steady_clock;
+    using time_point = clock::time_point;
+
     bool validStateTransition(CallState newState);
 
     void checkPendingIM();
@@ -433,6 +436,7 @@ private:
     std::string peerDisplayName_ {};
 
     time_t timestamp_start_ {0};
+    time_point duration_start_ {time_point::min()};
 
     ///< MultiDevice: message received by subcall to merged yet
     MsgList pendingInMessages_;
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index a56372339bbe08c761ea838000793f7628ba7952..24ee9c0e1a4b1b832373ce8b3708d5005106efd4 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -3789,6 +3789,13 @@ JamiAccount::requestPeerConnection(
                                          isVCard,
                                          channeledConnectedCb,
                                          onChanneledCancelled);
+    //auto convId = getOneToOneConversation(info.peer);
+    //if (!convId.empty()) {
+    //    Json::Value value;
+    //    value["tid"] = std::to_string(tid);
+    //    value["type"] = "application/data-transfer+json";
+    //    sendMessage(convId, value);
+    //}
 }
 
 void
@@ -4262,11 +4269,23 @@ JamiAccount::sendMessage(const std::string& conversationId,
                          const std::string& parent,
                          const std::string& type,
                          bool announce)
+{
+    Json::Value json;
+    json["body"] = message;
+    json["type"] = type;
+    sendMessage(conversationId, json, parent, announce);
+}
+
+void
+JamiAccount::sendMessage(const std::string& conversationId,
+                         const Json::Value& value,
+                         const std::string& parent,
+                         bool announce)
 {
     std::lock_guard<std::mutex> lk(conversationsMtx_);
     auto conversation = conversations_.find(conversationId);
     if (conversation != conversations_.end() && conversation->second) {
-        auto commitId = conversation->second->sendMessage(message, type, parent);
+        auto commitId = conversation->second->sendMessage(value, parent);
         if (!announce)
             return;
         if (!commitId.empty()) {
@@ -5267,4 +5286,19 @@ JamiAccount::announceMemberMessage(const std::string& convId,
                                                                        action);
     }
 }
+
+void
+JamiAccount::addCallHistoryMessage(const std::string& uri, uint64_t duration_ms)
+{
+    auto finalUri = uri.substr(0, uri.find("@ring.dht"));
+    auto convId = getOneToOneConversation(finalUri);
+    if (!convId.empty()) {
+        Json::Value value;
+        value["to"] = finalUri;
+        value["type"] = "application/call-history+json";
+        value["duration"] = std::to_string(duration_ms);
+        sendMessage(convId, value);
+    }
+}
+
 } // namespace jami
diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h
index e4f1e7055dd54d788bf8ba1dc3068919862e0fbd..4bbe027c557eb6e9abdc74f722d1cbc0a4703344 100644
--- a/src/jamidht/jamiaccount.h
+++ b/src/jamidht/jamiaccount.h
@@ -525,11 +525,21 @@ public:
         const std::string& conversationId);
 
     // Message send/load
+    void sendMessage(const std::string& conversationId,
+                     const Json::Value& value,
+                     const std::string& parent = "",
+                     bool announce = true);
     void sendMessage(const std::string& conversationId,
                      const std::string& message,
                      const std::string& parent = "",
                      const std::string& type = "text/plain",
                      bool announce = true);
+    /**
+     * Add to the related conversation the call history message
+     * @param uri           Peer number
+     * @param duration_ms   The call duration in ms
+     */
+    void addCallHistoryMessage(const std::string& uri, uint64_t duration_ms);
     uint32_t loadConversationMessages(const std::string& conversationId,
                                       const std::string& fromMessage = "",
                                       size_t n = 0);
@@ -963,15 +973,16 @@ private:
     void sendMessageNotification(const Conversation& conversation,
                                  const std::string& commitId,
                                  bool sync);
+
+    void announceMemberMessage(const std::string& convId,
+                               const std::map<std::string, std::string>& message) const;
+
     /**
      * Get related conversation with member
      * @param uri       The member to search for
      * @return the conversation id if found else empty
      */
     std::string getOneToOneConversation(const std::string& uri) const;
-
-    void announceMemberMessage(const std::string& convId,
-                               const std::map<std::string, std::string>& message) const;
 };
 
 static inline std::ostream&
diff --git a/test/unitTest/conversation/conversation.cpp b/test/unitTest/conversation/conversation.cpp
index 9cad8f1255e7adc5f4417d089778f76434f1c0ab..f9345749904ca6617d7b307724173fe2cc29dfc3 100644
--- a/test/unitTest/conversation/conversation.cpp
+++ b/test/unitTest/conversation/conversation.cpp
@@ -730,7 +730,7 @@ ConversationTest::testSendMessage()
     // Wait that alice sees Bob
     cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageAliceReceived == 1; });
 
-    aliceAccount->sendMessage(convId, "hi");
+    aliceAccount->sendMessage(convId, std::string("hi"));
     cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 1; });
     DRing::unregisterSignalHandlers();
 }
@@ -763,7 +763,7 @@ ConversationTest::testSendMessageTriggerMessageReceived()
     cv.wait_for(lk, std::chrono::seconds(30));
     CPPUNIT_ASSERT(conversationReady);
 
-    aliceAccount->sendMessage(convId, "hi");
+    aliceAccount->sendMessage(convId, std::string("hi"));
     cv.wait_for(lk, std::chrono::seconds(30), [&] { return messageReceived == 1; });
     CPPUNIT_ASSERT(messageReceived == 1);
     DRing::unregisterSignalHandlers();
@@ -819,9 +819,9 @@ ConversationTest::testMergeTwoDifferentHeads()
     ConversationRepository repo(carlaAccount, convId);
     repo.join();
 
-    aliceAccount->sendMessage(convId, "hi");
-    aliceAccount->sendMessage(convId, "sup");
-    aliceAccount->sendMessage(convId, "jami");
+    aliceAccount->sendMessage(convId, std::string("hi"));
+    aliceAccount->sendMessage(convId, std::string("sup"));
+    aliceAccount->sendMessage(convId, std::string("jami"));
 
     // Start Carla, should merge and all messages should be there
     Manager::instance().sendRegister(carlaId, true);
@@ -982,7 +982,7 @@ ConversationTest::testSendMessageToMultipleParticipants()
                + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + convId;
     CPPUNIT_ASSERT(fileutils::isDirectory(repoPath));
 
-    aliceAccount->sendMessage(convId, "hi");
+    aliceAccount->sendMessage(convId, std::string("hi"));
     CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&]() {
         return messageReceivedBob >= 1 && messageReceivedCarla >= 1;
     }));
@@ -1045,19 +1045,19 @@ ConversationTest::testPingPongMessages()
     CPPUNIT_ASSERT(fileutils::isDirectory(repoPath));
     messageBobReceived = 0;
     messageAliceReceived = 0;
-    aliceAccount->sendMessage(convId, "ping");
+    aliceAccount->sendMessage(convId, std::string("ping"));
     CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
         return messageBobReceived == 1 && messageAliceReceived == 1;
     }));
-    bobAccount->sendMessage(convId, "pong");
+    bobAccount->sendMessage(convId, std::string("pong"));
     CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
         return messageBobReceived == 2 && messageAliceReceived == 2;
     }));
-    bobAccount->sendMessage(convId, "ping");
+    bobAccount->sendMessage(convId, std::string("ping"));
     CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
         return messageBobReceived == 3 && messageAliceReceived == 3;
     }));
-    aliceAccount->sendMessage(convId, "pong");
+    aliceAccount->sendMessage(convId, std::string("pong"));
     CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
         return messageBobReceived == 4 && messageAliceReceived == 4;
     }));
@@ -1357,7 +1357,7 @@ ConversationTest::testBannedMemberCannotSendMessage()
 
     // Now check that alice doesn't receive a message from Bob
     aliceMessageReceived = false;
-    bobAccount->sendMessage(convId, "hi");
+    bobAccount->sendMessage(convId, std::string("hi"));
     CPPUNIT_ASSERT(
         !cv.wait_for(lk, std::chrono::seconds(30), [&]() { return aliceMessageReceived; }));
 }
@@ -1465,7 +1465,7 @@ ConversationTest::generateFakeVote(std::shared_ptr<JamiAccount> account,
     wbuilder["indentation"] = "";
     cr.commitMessage(Json::writeString(wbuilder, json));
 
-    account->sendMessage(convId, "trigger the fake history to be pulled");
+    account->sendMessage(convId, std::string("trigger the fake history to be pulled"));
 }
 
 void
@@ -1507,7 +1507,7 @@ ConversationTest::generateFakeInvite(std::shared_ptr<JamiAccount> account,
     wbuilder["indentation"] = "";
     cr.commitMessage(Json::writeString(wbuilder, json));
 
-    account->sendMessage(convId, "trigger the fake history to be pulled");
+    account->sendMessage(convId, std::string("trigger the fake history to be pulled"));
 }
 void
 ConversationTest::addAll(std::shared_ptr<JamiAccount> account, const std::string& convId)
@@ -1649,7 +1649,7 @@ ConversationTest::testMemberCannotBanOther()
     generateFakeVote(carlaAccount, convId, bobUri);
     CPPUNIT_ASSERT(!cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived; }));
 
-    aliceAccount->sendMessage(convId, "hi");
+    aliceAccount->sendMessage(convId, std::string("hi"));
     CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived; }));
 }
 
@@ -1832,7 +1832,7 @@ ConversationTest::testPlainTextNoBadFile()
     cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageAliceReceived == 1; });
 
     addFile(aliceAccount, convId, "BADFILE");
-    aliceAccount->sendMessage(convId, "hi");
+    aliceAccount->sendMessage(convId, std::string("hi"));
     // Check not received due to the unwanted file
     CPPUNIT_ASSERT(
         !cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 1; }));
@@ -1924,7 +1924,7 @@ ConversationTest::testVoteNoBadFile()
     }));
 
     messageCarlaReceived = false;
-    bobAccount->sendMessage(convId, "final");
+    bobAccount->sendMessage(convId, std::string("final"));
     CPPUNIT_ASSERT(
         cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageCarlaReceived; }));
 }