diff --git a/bin/dbus/cx.ring.Ring.CallManager.xml b/bin/dbus/cx.ring.Ring.CallManager.xml
index a8adc1e659a337e3507f7c8044d44bfcd2e1d965..97ab3b0e6ceb05dc77c8b3c16a5890cade0bb9a3 100644
--- a/bin/dbus/cx.ring.Ring.CallManager.xml
+++ b/bin/dbus/cx.ring.Ring.CallManager.xml
@@ -352,6 +352,14 @@
             <arg type="b" name="isParticipant" direction="out"/>
         </method>
 
+        <method name="raiseParticipantHand" tp:name-for-bindings="raiseParticipantHand">
+            <tp:added version="11.0.0"/>
+            <arg type="s" name="accountId" direction="in"/>
+            <arg type="s" name="confId" direction="in"/>
+            <arg type="s" name="peerId" direction="in"/>
+            <arg type="b" name="state" direction="in"/>
+        </method>
+
         <method name="hangupParticipant" tp:name-for-bindings="hangupParticipant">
             <tp:added version="9.8.0"/>
             <arg type="s" name="confId" direction="in"/>
diff --git a/bin/dbus/dbuscallmanager.cpp b/bin/dbus/dbuscallmanager.cpp
index 30fe21d08c3a13e7282aef137dafd31200f37fe2..dc652ef555b6c7e3606f44fa0e18426bc2426860 100644
--- a/bin/dbus/dbuscallmanager.cpp
+++ b/bin/dbus/dbuscallmanager.cpp
@@ -376,4 +376,13 @@ void
 DBusCallManager::hangupParticipant(const std::string& confId, const std::string& peerId)
 {
     DRing::hangupParticipant(confId, peerId);
+}
+
+void
+DBusCallManager::raiseParticipantHand(const std::string& accountId,
+                                      const std::string& confId,
+                                      const std::string& peerId,
+                                      const bool& state)
+{
+    DRing::raiseParticipantHand(accountId, confId, peerId, state);
 }
\ No newline at end of file
diff --git a/bin/dbus/dbuscallmanager.h b/bin/dbus/dbuscallmanager.h
index 333dbf40a4e3359a7d767f46085ca04ab98e8bbc..f5dd35035758fe468b03155d6e7c69517dfdef9e 100644
--- a/bin/dbus/dbuscallmanager.h
+++ b/bin/dbus/dbuscallmanager.h
@@ -118,6 +118,10 @@ public:
     void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
     void muteParticipant(const std::string& confId, const std::string& peerId, const bool& state);
     void hangupParticipant(const std::string& confId, const std::string& peerId);
+    void raiseParticipantHand(const std::string& accountId,
+                              const std::string& confId,
+                              const std::string& peerId,
+                              const bool& state);
 };
 
 #endif // __RING_CALLMANAGER_H__
diff --git a/bin/jni/callmanager.i b/bin/jni/callmanager.i
index ec00bd1c271223e8048110f3ea5a825e0f6dc6ab..b1ad57fd833ef7b4b737e9ac4d8a4a975df71de0 100644
--- a/bin/jni/callmanager.i
+++ b/bin/jni/callmanager.i
@@ -107,6 +107,7 @@ std::vector<std::map<std::string, std::string>> getConferenceInfos(const std::st
 void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
 void muteParticipant(const std::string& confId, const std::string& peerId, const bool& state);
 void hangupParticipant(const std::string& confId, const std::string& peerId);
+void raiseParticipantHand(const std::string& accountId, const std::string& confId, const std::string& peerId, const bool& state);
 
 /* File Playback methods */
 bool startRecordedFilePlayback(const std::string& filepath);
diff --git a/bin/nodejs/callmanager.i b/bin/nodejs/callmanager.i
index b7a1b1ee7edb3f993a295454803a182c5ca55634..2812acb632a076b6421a31fea17548c71bf3b1e4 100644
--- a/bin/nodejs/callmanager.i
+++ b/bin/nodejs/callmanager.i
@@ -96,6 +96,7 @@ std::vector<std::map<std::string, std::string>> getConferenceInfos(const std::st
 void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
 void muteParticipant(const std::string& confId, const std::string& peerId, const bool& state);
 void hangupParticipant(const std::string& confId, const std::string& peerId);
+void raiseParticipantHand(const std::string& accountId, const std::string& confId, const std::string& peerId, const bool& state);
 
 /* File Playback methods */
 bool startRecordedFilePlayback(const std::string& filepath);
diff --git a/src/client/callmanager.cpp b/src/client/callmanager.cpp
index cf84f8e98bfb162eb40507d959523fa408e335e1..c5dc9e11823cd5bd3a0dc9249cb4314219c0707a 100644
--- a/src/client/callmanager.cpp
+++ b/src/client/callmanager.cpp
@@ -390,6 +390,15 @@ muteParticipant(const std::string& confId, const std::string& peerId, const bool
     jami::Manager::instance().muteParticipant(confId, peerId, state);
 }
 
+void
+raiseParticipantHand(const std::string& accountId,
+                     const std::string& confId,
+                     const std::string& peerId,
+                     const bool& state)
+{
+    jami::Manager::instance().raiseParticipantHand(confId, peerId, state);
+}
+
 void
 hangupParticipant(const std::string& confId, const std::string& participant)
 {
diff --git a/src/conference.cpp b/src/conference.cpp
index 5206471576050e6a72ae189ebd27b603b2f537cc..c4a4a7512941f78a2371d9b8fa5b5a711a3adf9c 100644
--- a/src/conference.cpp
+++ b/src/conference.cpp
@@ -105,6 +105,7 @@ Conference::Conference(bool enableVideo)
                     peerId = "host"sv;
                     isLocalMuted = shared->isMediaSourceMuted(MediaType::MEDIA_AUDIO);
                 }
+                auto isHandRaised = shared->isHandRaised(peerId);
                 auto isModeratorMuted = shared->isMuted(peerId);
                 auto sinkId = shared->getConfID() + peerId;
                 newInfo.emplace_back(ParticipantInfo {std::move(uri),
@@ -118,7 +119,8 @@ Conference::Conference(bool enableVideo)
                                                       !info.hasVideo,
                                                       isLocalMuted,
                                                       isModeratorMuted,
-                                                      isModerator});
+                                                      isModerator,
+                                                      isHandRaised});
             }
             if (auto videoMixer = shared->getVideoMixer()) {
                 newInfo.h = videoMixer->getHeight();
@@ -127,8 +129,11 @@ Conference::Conference(bool enableVideo)
             lk.unlock();
             if (!hostAdded) {
                 auto audioLocalMuted = shared->isMediaSourceMuted(MediaType::MEDIA_AUDIO);
-                newInfo.emplace_back(ParticipantInfo {
-                    "", "", "", false, 0, 0, 0, 0, true, audioLocalMuted, false, true});
+                ParticipantInfo pi;
+                pi.videoMuted = true;
+                pi.audioLocalMuted = shared->isMediaSourceMuted(MediaType::MEDIA_AUDIO);
+                pi.isModerator = true;
+                newInfo.emplace_back(pi);
             }
 
             shared->updateConferenceInfo(std::move(newInfo));
@@ -433,7 +438,6 @@ Conference::addParticipant(const std::string& participant_id)
 void
 Conference::setActiveParticipant(const std::string& participant_id)
 {
-    // TODO. Shouldn't be protected by ENABLE_VIDEO define ?
     if (!videoMixer_)
         return;
     if (isHost(participant_id)) {
@@ -570,6 +574,7 @@ Conference::removeParticipant(const std::string& participant_id)
     if (participants_.erase(participant_id)) {
         if (auto call = getCall(participant_id)) {
             participantsMuted_.erase(std::string(string_remove_suffix(call->getPeerNumber(), '@')));
+            handsRaised_.erase(std::string(string_remove_suffix(call->getPeerNumber(), '@')));
 #ifdef ENABLE_VIDEO
             call->exitConference();
             if (call->isPeerRecording())
@@ -871,6 +876,10 @@ Conference::onConfOrder(const std::string& callId, const std::string& confOrder)
         if (root.isMember("hangupParticipant")) {
             hangupParticipant(root["hangupParticipant"].asString());
         }
+        if (root.isMember("handRaised")) {
+            setHandRaised(root["handRaised"].asString(),
+                                 root["handState"].asString() == "true");
+        }
     }
 }
 
@@ -886,6 +895,50 @@ Conference::isModerator(std::string_view uri) const
     return moderators_.find(uri) != moderators_.end() or isHost(uri);
 }
 
+bool
+Conference::isHandRaised(std::string_view uri) const
+{
+    return isHost(uri) ? handsRaised_.find("host"sv) != handsRaised_.end()
+                       : handsRaised_.find(uri) != handsRaised_.end();
+}
+
+void
+Conference::setHandRaised(const std::string& participant_id, const bool& state)
+{
+    if (isHost(participant_id)) {
+        auto isPeerRequiringAttention = isHandRaised("host"sv);
+        if (state and not isPeerRequiringAttention) {
+            JAMI_DBG("Raise host hand");
+            handsRaised_.emplace("host"sv);
+            updateHandsRaised();
+        } else if (not state and isPeerRequiringAttention) {
+            JAMI_DBG("Lower host hand");
+            handsRaised_.erase("host");
+            updateHandsRaised();
+        }
+        return;
+    } else {
+        for (const auto& p : participants_) {
+            if (auto call = getCall(p)) {
+                auto isPeerRequiringAttention = isHandRaised(participant_id);
+                if (participant_id == string_remove_suffix(call->getPeerNumber(), '@')) {
+                    if (state and not isPeerRequiringAttention) {
+                        JAMI_DBG("Raise %s hand", participant_id.c_str());
+                        handsRaised_.emplace(participant_id);
+                        updateHandsRaised();
+                    } else if (not state and isPeerRequiringAttention) {
+                        JAMI_DBG("Remove %s raised hand", participant_id.c_str());
+                        handsRaised_.erase(participant_id);
+                        updateHandsRaised();
+                    }
+                    return;
+                }
+            }
+        }
+    }
+    JAMI_WARN("Fail to raise %s hand (participant not found)", participant_id.c_str());
+}
+
 void
 Conference::setModerator(const std::string& participant_id, const bool& state)
 {
@@ -919,6 +972,16 @@ Conference::updateModerators()
     sendConferenceInfos();
 }
 
+void
+Conference::updateHandsRaised()
+{
+    std::lock_guard<std::mutex> lk(confInfoMutex_);
+    for (auto& info : confInfo_) {
+        info.handRaised = isHandRaised(string_remove_suffix(info.uri, '@'));
+    }
+    sendConferenceInfos();
+}
+
 bool
 Conference::isMuted(std::string_view uri) const
 {
diff --git a/src/conference.h b/src/conference.h
index 143319f1cf077960f739746962747026e8203aea..424fa4e638bc45a6675d2fe822d19e1f8d6670a8 100644
--- a/src/conference.h
+++ b/src/conference.h
@@ -70,6 +70,7 @@ struct ParticipantInfo
     bool audioLocalMuted {false};
     bool audioModeratorMuted {false};
     bool isModerator {false};
+    bool handRaised {false};
 
     void fromJson(const Json::Value& v)
     {
@@ -85,6 +86,7 @@ struct ParticipantInfo
         audioLocalMuted = v["audioLocalMuted"].asBool();
         audioModeratorMuted = v["audioModeratorMuted"].asBool();
         isModerator = v["isModerator"].asBool();
+        handRaised = v["handRaised"].asBool();
     }
 
     Json::Value toJson() const
@@ -102,6 +104,7 @@ struct ParticipantInfo
         val["audioLocalMuted"] = audioLocalMuted;
         val["audioModeratorMuted"] = audioModeratorMuted;
         val["isModerator"] = isModerator;
+        val["handRaised"] = handRaised;
         return val;
     }
 
@@ -118,7 +121,8 @@ struct ParticipantInfo
                 {"videoMuted", videoMuted ? "true" : "false"},
                 {"audioLocalMuted", audioLocalMuted ? "true" : "false"},
                 {"audioModeratorMuted", audioModeratorMuted ? "true" : "false"},
-                {"isModerator", isModerator ? "true" : "false"}};
+                {"isModerator", isModerator ? "true" : "false"},
+                {"handRaised", handRaised ? "true" : "false"}};
     }
 
     friend bool operator==(const ParticipantInfo& p1, const ParticipantInfo& p2)
@@ -128,7 +132,7 @@ struct ParticipantInfo
                and p1.h == p2.h and p1.videoMuted == p2.videoMuted
                and p1.audioLocalMuted == p2.audioLocalMuted
                and p1.audioModeratorMuted == p2.audioModeratorMuted
-               and p1.isModerator == p2.isModerator;
+               and p1.isModerator == p2.isModerator and p1.handRaised == p2.handRaised;
     }
 
     friend bool operator!=(const ParticipantInfo& p1, const ParticipantInfo& p2)
@@ -320,6 +324,7 @@ public:
     void updateConferenceInfo(ConfInfo confInfo);
     void createSinks(const ConfInfo& infos);
     void setModerator(const std::string& uri, const bool& state);
+    void setHandRaised(const std::string& uri, const bool& state);
     void muteParticipant(const std::string& uri, const bool& state);
     void hangupParticipant(const std::string& participant_id);
     void updateMuted();
@@ -335,7 +340,9 @@ private:
 
     static std::shared_ptr<Call> getCall(const std::string& callId);
     bool isModerator(std::string_view uri) const;
+    bool isHandRaised(std::string_view uri) const;
     void updateModerators();
+    void updateHandsRaised();
 
     std::string id_;
     State confState_ {State::ACTIVE_ATTACHED};
@@ -361,6 +368,7 @@ private:
     std::shared_ptr<jami::AudioInput> audioMixer_;
     std::set<std::string, std::less<>> moderators_ {};
     std::set<std::string, std::less<>> participantsMuted_ {};
+    std::set<std::string, std::less<>> handsRaised_;
 
     void initRecorder(std::shared_ptr<MediaRecorder>& rec);
     void deinitRecorder(std::shared_ptr<MediaRecorder>& rec);
diff --git a/src/jami/callmanager_interface.h b/src/jami/callmanager_interface.h
index 944b4d43be083a2f0d2074257b0b85059f432998..0c3fef7b00bc4cff427a43ec59b2b48c234861c9 100644
--- a/src/jami/callmanager_interface.h
+++ b/src/jami/callmanager_interface.h
@@ -97,6 +97,10 @@ DRING_PUBLIC void setModerator(const std::string& confId,
 DRING_PUBLIC void muteParticipant(const std::string& confId,
                                   const std::string& peerId,
                                   const bool& state);
+DRING_PUBLIC void raiseParticipantHand(const std::string& accountId,
+                                       const std::string& confId,
+                                       const std::string& peerId,
+                                       const bool& state);
 DRING_PUBLIC void hangupParticipant(const std::string& confId, const std::string& participant);
 
 /* Statistic related methods */
diff --git a/src/manager.cpp b/src/manager.cpp
index 305cc12925f41c57823a6f8bbc03c24841fa1d0d..fd8dc23b0a6c4be4897e2edb911499c026c45c9a 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -3583,6 +3583,22 @@ Manager::muteParticipant(const std::string& confId,
     }
 }
 
+void
+Manager::raiseParticipantHand(const std::string& confId,
+                              const std::string& participant,
+                              const bool& state)
+{
+    if (auto conf = getConferenceFromID(confId)) {
+        conf->setHandRaised(participant, state);
+    } else if (auto call = getCallFromCallID(confId)) {
+        std::map<std::string, std::string> messages;
+        Json::Value root;
+        root["handRaised"] = participant;
+        root["handState"] = state ? TRUE_STR : FALSE_STR;
+        call->sendConfOrder(root);
+    }
+}
+
 void
 Manager::setDefaultModerator(const std::string& accountID, const std::string& peerURI, bool state)
 {
diff --git a/src/manager.h b/src/manager.h
index 5accbd5461c833c26cc88b6d9f883fe5e7da2eb9..464d8ba711f967616eedc7d999a915d51e5cb107 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -1023,6 +1023,9 @@ public:
     void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
     void muteParticipant(const std::string& confId, const std::string& peerId, const bool& state);
     void hangupParticipant(const std::string& confId, const std::string& participant);
+    void raiseParticipantHand(const std::string& confId,
+                              const std::string& participant,
+                              const bool& state);
 
     void setDefaultModerator(const std::string& accountID, const std::string& peerURI, bool state);
     std::vector<std::string> getDefaultModerators(const std::string& accountID);
diff --git a/test/unitTest/call/conference.cpp b/test/unitTest/call/conference.cpp
index 1b12ad9e6ba8feef6fff7d41ffebe2fa16a5d13e..71bfd9f9ef2f1b2aabb5210c4e430a480277f00c 100644
--- a/test/unitTest/call/conference.cpp
+++ b/test/unitTest/call/conference.cpp
@@ -43,12 +43,14 @@ struct CallData
     std::string callId {};
     std::string state {};
     std::atomic_bool moderatorMuted {false};
+    std::atomic_bool raisedHand {false};
 
     void reset()
     {
         callId = "";
         state = "";
         moderatorMuted = false;
+        raisedHand = false;
     }
 };
 
@@ -73,6 +75,7 @@ private:
     void testAudioVideoMutedStates();
     void testCreateParticipantsSinks();
     void testMuteStatusAfterRemove();
+    void testHandsUp();
 
     CPPUNIT_TEST_SUITE(ConferenceTest);
     CPPUNIT_TEST(testGetConference);
@@ -80,6 +83,7 @@ private:
     CPPUNIT_TEST(testAudioVideoMutedStates);
     CPPUNIT_TEST(testCreateParticipantsSinks);
     CPPUNIT_TEST(testMuteStatusAfterRemove);
+    CPPUNIT_TEST(testHandsUp);
     CPPUNIT_TEST_SUITE_END();
 
     // Common parts
@@ -179,10 +183,13 @@ ConferenceTest::registerSignalHandlers()
             for (const auto& infos : participantsInfos) {
                 if (infos.at("uri").find(bobUri) != std::string::npos) {
                     bobCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
+                    bobCall.raisedHand = infos.at("handRaised") == "true";
                 } else if (infos.at("uri").find(carlaUri) != std::string::npos) {
                     carlaCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
+                    carlaCall.raisedHand = infos.at("handRaised") == "true";
                 } else if (infos.at("uri").find(daviUri) != std::string::npos) {
                     daviCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
+                    daviCall.raisedHand = infos.at("handRaised") == "true";
                 }
             }
             cv.notify_one();
@@ -405,6 +412,65 @@ ConferenceTest::testMuteStatusAfterRemove()
 
     DRing::unregisterSignalHandlers();
 }
+
+void
+ConferenceTest::testHandsUp()
+{
+    registerSignalHandlers();
+
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobUri = bobAccount->getUsername();
+    auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
+    auto daviUri = daviAccount->getUsername();
+
+    startConference();
+
+    JAMI_INFO("Play with raise hand");
+    Manager::instance().raiseParticipantHand(confId, bobUri, true);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(5), [&] { return bobCall.raisedHand.load(); }));
+
+    Manager::instance().raiseParticipantHand(confId, bobUri, false);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(5), [&] { return !bobCall.raisedHand.load(); }));
+
+    JAMI_INFO("Start call between Alice and Davi");
+    auto call1 = aliceAccount->newOutgoingCall(daviUri);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return !daviCall.callId.empty(); }));
+    Manager::instance().answerCall(daviCall.callId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return daviCall.state == "CURRENT"; }));
+    Manager::instance().addParticipant(daviCall.callId, confId);
+
+    Manager::instance().raiseParticipantHand(confId, daviUri, true);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(5), [&] { return daviCall.raisedHand.load(); }));
+
+    Manager::instance().hangupCall(daviCall.callId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return daviCall.state == "OVER"; }));
+    daviCall.reset();
+
+    auto call2 = aliceAccount->newOutgoingCall(daviUri);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return !daviCall.callId.empty(); }));
+    Manager::instance().answerCall(daviCall.callId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return daviCall.state == "CURRENT"; }));
+    Manager::instance().addParticipant(daviCall.callId, confId);
+
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(5), [&] { return !daviCall.raisedHand.load(); }));
+
+    Manager::instance().hangupCall(daviCall.callId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return daviCall.state == "OVER"; }));
+    hangupConference();
+
+    DRing::unregisterSignalHandlers();
+}
 } // namespace test
 } // namespace jami