diff --git a/src/call.cpp b/src/call.cpp
index db8736fcd6c038f8392827300b57f124fb09c3d3..919e6f830aec2098f1ef0c71cdf41a34ecc93b37 100644
--- a/src/call.cpp
+++ b/src/call.cpp
@@ -419,6 +419,13 @@ Call::onTextMessage(std::map<std::string, std::string>&& messages)
         return;
     }
 
+    it = messages.find("application/confOrder+json");
+    if (it != messages.end()) {
+        if (auto conf = Manager::instance().getConferenceFromID(confID_))
+            conf->onConfOrder(getCallId(), it->second);
+        return;
+    }
+
     {
         std::lock_guard<std::recursive_mutex> lk {callMutex_};
         if (parent_) {
diff --git a/src/conference.cpp b/src/conference.cpp
index 03e7f2cc14185aa92dc5034d944597d6f68e44c5..6bf29a15f7f6d7b753bc34092f87db229883aa7e 100644
--- a/src/conference.cpp
+++ b/src/conference.cpp
@@ -19,12 +19,14 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
+#include <regex>
 #include <sstream>
 
 #include "conference.h"
 #include "manager.h"
 #include "audio/audiolayer.h"
 #include "audio/ringbufferpool.h"
+#include "jamidht/jamiaccount.h"
 
 #ifdef ENABLE_VIDEO
 #include "sip/sipcall.h"
@@ -47,6 +49,17 @@ Conference::Conference()
 {
     JAMI_INFO("Create new conference %s", id_.c_str());
 
+    // TODO: For now, add all accounts on the same device as
+    // conference master. In the future, this should be
+    // retrieven with another way
+    auto accounts = jami::Manager::instance().getAllAccounts<JamiAccount>();
+    moderators_.reserve(accounts.size());
+    for (const auto& account : accounts) {
+        if (!account)
+            continue;
+        moderators_.emplace_back(account->getUsername());
+    }
+
 #ifdef ENABLE_VIDEO
     getVideoMixer()->setOnSourcesUpdated([this](const std::vector<video::SourceInfo>&& infos) {
         runOnMainThread([w = weak(), infos = std::move(infos)] {
@@ -79,8 +92,15 @@ Conference::Conference()
                                  and not videoMixer->getActiveParticipant()); // by default, local
                                                                               // is shown as active
                 subCalls.erase(it->second);
-                newInfo.emplace_back(ParticipantInfo {
-                    std::move(uri), active, info.x, info.y, info.w, info.h, !info.hasVideo, false});
+                newInfo.emplace_back(ParticipantInfo {std::move(uri),
+                                                      active,
+                                                      info.x,
+                                                      info.y,
+                                                      info.w,
+                                                      info.h,
+                                                      !info.hasVideo,
+                                                      false,
+                                                      shared->isModerator(uri)});
             }
             lk.unlock();
             // Handle participants not present in the video mixer
@@ -88,7 +108,15 @@ Conference::Conference()
                 std::string uri = "";
                 if (auto call = Manager::instance().callFactory.getCall<SIPCall>(subCall))
                     uri = call->getPeerNumber();
-                ParticipantInfo {std::move(uri), false, 0, 0, 0, 0, true, false};
+                ParticipantInfo {std::move(uri),
+                                 false,
+                                 0,
+                                 0,
+                                 0,
+                                 0,
+                                 true,
+                                 false,
+                                 shared->isModerator(uri)};
             }
 
             {
@@ -119,7 +147,8 @@ Conference::~Conference()
                 JAMI_DBG("Stop recording for conf %s", getConfID().c_str());
                 this->toggleRecording();
                 if (not call->isRecording()) {
-                    JAMI_DBG("Conference was recorded, start recording for conf %s", call->getCallId().c_str());
+                    JAMI_DBG("Conference was recorded, start recording for conf %s",
+                             call->getCallId().c_str());
                     call->toggleRecording();
                 }
             }
@@ -155,12 +184,12 @@ Conference::add(const std::string& participant_id)
                 JAMI_DBG("Stop recording for call %s", call->getCallId().c_str());
                 call->toggleRecording();
                 if (not this->isRecording()) {
-                    JAMI_DBG("One participant was recording, start recording for conference %s", getConfID().c_str());
+                    JAMI_DBG("One participant was recording, start recording for conference %s",
+                             getConfID().c_str());
                     this->toggleRecording();
                 }
             }
-        }
-        else
+        } else
             JAMI_ERR("no call associate to participant %s", participant_id.c_str());
 #endif // ENABLE_VIDEO
     }
@@ -172,8 +201,9 @@ Conference::setActiveParticipant(const std::string& participant_id)
     if (!videoMixer_)
         return;
     for (const auto& item : participants_) {
-        if (participant_id == item) {
-            if (auto call = Manager::instance().callFactory.getCall<SIPCall>(participant_id)) {
+        if (auto call = Manager::instance().callFactory.getCall<SIPCall>(item)) {
+            if (participant_id == item
+                || call->getPeerNumber().find(participant_id) != std::string::npos) {
                 videoMixer_->setActiveParticipant(call->getVideoRtp().getVideoReceive().get());
                 return;
             }
@@ -183,6 +213,24 @@ Conference::setActiveParticipant(const std::string& participant_id)
     videoMixer_->setActiveParticipant(nullptr);
 }
 
+void
+Conference::setLayout(int layout)
+{
+    switch (layout) {
+    case 0:
+        getVideoMixer()->setVideoLayout(video::Layout::GRID);
+        break;
+    case 1:
+        getVideoMixer()->setVideoLayout(video::Layout::ONE_BIG_WITH_SMALL);
+        break;
+    case 2:
+        getVideoMixer()->setVideoLayout(video::Layout::ONE_BIG);
+        break;
+    default:
+        break;
+    }
+}
+
 std::vector<std::map<std::string, std::string>>
 ConfInfo::toVectorMapStringString() const
 {
@@ -434,4 +482,46 @@ Conference::deinitRecorder(std::shared_ptr<MediaRecorder>& rec)
     ghostRingBuffer_.reset();
 }
 
+void
+Conference::onConfOrder(const std::string& callId, const std::string& confOrder)
+{
+    // Check if the peer is a master
+    if (auto call = Manager::instance().getCallFromCallID(callId)) {
+        auto uri = call->getPeerNumber();
+        auto separator = uri.find('@');
+        if (separator != std::string::npos)
+            uri = uri.substr(0, separator - 1);
+        if (!isModerator(uri)) {
+            JAMI_WARN("Received conference order from a non master (%s)", uri.c_str());
+            return;
+        }
+
+        std::string err;
+        Json::Value root;
+        Json::CharReaderBuilder rbuilder;
+        auto reader = std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
+        if (!reader->parse(confOrder.c_str(), confOrder.c_str() + confOrder.size(), &root, &err)) {
+            JAMI_WARN("Couldn't parse conference order from %s", uri.c_str());
+            return;
+        }
+        if (root.isMember("layout")) {
+            setLayout(root["layout"].asUInt());
+        }
+        if (root.isMember("activeParticipant")) {
+            setActiveParticipant(root["activeParticipant"].asString());
+        }
+    }
+}
+
+bool
+Conference::isModerator(const std::string& uri) const
+{
+    return std::find_if(moderators_.begin(),
+                        moderators_.end(),
+                        [&uri](const std::string& master) {
+                            return master.find(uri) != std::string::npos;
+                        })
+           != moderators_.end();
+}
+
 } // namespace jami
diff --git a/src/conference.h b/src/conference.h
index 48955e1175b34a5c6a6f6317d4b740b5e550c32b..709274b236e0b28492c107e17426d61767efcdac 100644
--- a/src/conference.h
+++ b/src/conference.h
@@ -31,7 +31,6 @@
 
 #include "audio/audio_input.h"
 
-
 #include <json/json.h>
 
 #include "recordable.h"
@@ -54,6 +53,7 @@ struct ParticipantInfo
     int h {0};
     bool videoMuted {false};
     bool audioMuted {false};
+    bool isModerator {false};
 
     void fromJson(const Json::Value& v)
     {
@@ -65,6 +65,7 @@ struct ParticipantInfo
         h = v["h"].asInt();
         videoMuted = v["videoMuted"].asBool();
         audioMuted = v["audioMuted"].asBool();
+        isModerator = v["isModerator"].asBool();
     }
 
     Json::Value toJson() const
@@ -78,6 +79,7 @@ struct ParticipantInfo
         val["h"] = h;
         val["videoMuted"] = videoMuted;
         val["audioMuted"] = audioMuted;
+        val["isModerator"] = isModerator;
         return val;
     }
 
@@ -90,7 +92,8 @@ struct ParticipantInfo
                 {"w", std::to_string(w)},
                 {"h", std::to_string(h)},
                 {"videoMuted", videoMuted ? "true" : "false"},
-                {"audioMuted", audioMuted ? "true" : "false"}};
+                {"audioMuted", audioMuted ? "true" : "false"},
+                {"isModerator", isModerator ? "true" : "false"}};
     }
 };
 
@@ -193,10 +196,13 @@ public:
     void switchInput(const std::string& input);
 
     void setActiveParticipant(const std::string& participant_id);
+    void setLayout(int layout);
 
     void attachVideo(Observable<std::shared_ptr<MediaFrame>>* frame, const std::string& callId);
     void detachVideo(Observable<std::shared_ptr<MediaFrame>>* frame);
 
+    void onConfOrder(const std::string& callId, const std::string& order);
+
 #ifdef ENABLE_VIDEO
     std::shared_ptr<video::VideoMixer> getVideoMixer();
     std::string getVideoInput() const { return mediaInput_; }
@@ -214,6 +220,8 @@ private:
         return std::static_pointer_cast<Conference>(shared_from_this());
     }
 
+    bool isModerator(const std::string& uri) const;
+
     std::string id_;
     State confState_ {State::ACTIVE_ATTACHED};
     ParticipantSet participants_;
@@ -232,6 +240,7 @@ private:
 #endif
 
     std::shared_ptr<jami::AudioInput> audioMixer_;
+    std::vector<std::string> moderators_ {};
 
     void initRecorder(std::shared_ptr<MediaRecorder>& rec);
     void deinitRecorder(std::shared_ptr<MediaRecorder>& rec);
diff --git a/src/manager.cpp b/src/manager.cpp
index e1edd87eeeed3ff29521ce4373ac4d06b0fea8e2..cb6f8cd100636496233a62f946b7e8d1e603c92d 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -1469,28 +1469,35 @@ void
 Manager::setConferenceLayout(const std::string& confId, int layout)
 {
     if (auto conf = getConferenceFromID(confId)) {
-        auto videoMixer = conf->getVideoMixer();
-        switch (layout) {
-        case 0:
-            videoMixer->setVideoLayout(video::Layout::GRID);
-            break;
-        case 1:
-            videoMixer->setVideoLayout(video::Layout::ONE_BIG_WITH_SMALL);
-            break;
-        case 2:
-            videoMixer->setVideoLayout(video::Layout::ONE_BIG);
-            break;
-        default:
-            break;
-        }
+        conf->setLayout(layout);
+    } else if (auto call = getCallFromCallID(confId)) {
+        std::map<std::string, std::string> messages;
+        Json::Value root;
+        root["layout"] = layout;
+        Json::StreamWriterBuilder wbuilder;
+        wbuilder["commentStyle"] = "None";
+        wbuilder["indentation"] = "";
+        auto output = Json::writeString(wbuilder, root);
+        messages["application/confOrder+json"] = output;
+        call->sendTextMessage(messages, call->getPeerDisplayName());
     }
 }
 
 void
-Manager::setActiveParticipant(const std::string& confId, const std::string& callId)
+Manager::setActiveParticipant(const std::string& confId, const std::string& participant)
 {
     if (auto conf = getConferenceFromID(confId)) {
-        conf->setActiveParticipant(callId);
+        conf->setActiveParticipant(participant);
+    } else if (auto call = getCallFromCallID(confId)) {
+        std::map<std::string, std::string> messages;
+        Json::Value root;
+        root["activeParticipant"] = participant;
+        Json::StreamWriterBuilder wbuilder;
+        wbuilder["commentStyle"] = "None";
+        wbuilder["indentation"] = "";
+        auto output = Json::writeString(wbuilder, root);
+        messages["application/confOrder+json"] = output;
+        call->sendTextMessage(messages, call->getPeerDisplayName());
     }
 }