diff --git a/back/src/Controller/BaseController.ts b/back/src/Controller/BaseController.ts
index 0b744082303b814b7e3c59155501e9ec726b026e..93c17ab41b21f2643c067de2c5acde7929c262b7 100644
--- a/back/src/Controller/BaseController.ts
+++ b/back/src/Controller/BaseController.ts
@@ -1,5 +1,4 @@
-import {HttpRequest, HttpResponse} from "uWebSockets.js";
-import {ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable";
+import {HttpResponse} from "uWebSockets.js";
 
 
 export class BaseController {
diff --git a/back/src/Controller/DebugController.ts b/back/src/Controller/DebugController.ts
index a94cc61620e16c91b16c40548c72ac29c510b0cb..509d8b2f52b6564fc783af9032771b2840f7593f 100644
--- a/back/src/Controller/DebugController.ts
+++ b/back/src/Controller/DebugController.ts
@@ -4,7 +4,6 @@ import {HttpRequest, HttpResponse} from "uWebSockets.js";
 import { parse } from 'query-string';
 import {App} from "../Server/sifrr.server";
 import {socketManager} from "../Services/SocketManager";
-import {ServerWritableStream} from "grpc";
 
 export class DebugController {
     constructor(private App : App) {
diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts
index b6a82850bdea1cad8169f3d1b6d83c3be0161545..4302a141247f5366a033de3fe48fe0bb188440d9 100644
--- a/back/src/RoomManager.ts
+++ b/back/src/RoomManager.ts
@@ -2,7 +2,8 @@ import {IRoomManagerServer} from "./Messages/generated/messages_grpc_pb";
 import {
     AdminGlobalMessage,
     AdminMessage,
-    AdminPusherToBackMessage,
+    AdminPusherToBackMessage, 
+    AdminRoomMessage,
     BanMessage,
     EmptyMessage,
     ItemEventMessage,
@@ -51,12 +52,8 @@ const roomManager: IRoomManagerServer = {
                 } else {
                     if (message.hasJoinroommessage()) {
                         throw new Error('Cannot call JoinRoomMessage twice!');
-                    /*} else if (message.hasViewportmessage()) {
-                        socketManager.handleViewport(client, message.getViewportmessage() as ViewportMessage);*/
                     } else if (message.hasUsermovesmessage()) {
                         socketManager.handleUserMovesMessage(room, user, message.getUsermovesmessage() as UserMovesMessage);
-                        /*} else if (message.hasSetplayerdetailsmessage()) {
-                            socketManager.handleSetPlayerDetails(client, message.getSetplayerdetailsmessage() as SetPlayerDetailsMessage);*/
                     } else if (message.hasSilentmessage()) {
                         socketManager.handleSilentMessage(room, user, message.getSilentmessage() as SilentMessage);
                     } else if (message.hasItemeventmessage()) {
@@ -67,8 +64,6 @@ const roomManager: IRoomManagerServer = {
                         socketManager.emitScreenSharing(room, user, message.getWebrtcscreensharingsignaltoservermessage() as WebRtcSignalToServerMessage);
                     } else if (message.hasPlayglobalmessage()) {
                         socketManager.emitPlayGlobalMessage(room, message.getPlayglobalmessage() as PlayGlobalMessage);
-                    /*} else if (message.hasReportplayermessage()){
-                        socketManager.handleReportMessage(client, message.getReportplayermessage() as ReportPlayerMessage);*/
                     } else if (message.hasQueryjitsijwtmessage()){
                         socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
                     }else if (message.hasSendusermessage()) {
@@ -119,10 +114,7 @@ const roomManager: IRoomManagerServer = {
             socketManager.removeZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY());
             call.end();
         })
-
-        /*call.on('finish', () => {
-            debug('listenZone finish');
-        })*/
+        
         call.on('close', () => {
             debug('listenZone connection closed');
             socketManager.removeZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY());
@@ -150,26 +142,6 @@ const roomManager: IRoomManagerServer = {
                     } else {
                         throw new Error('The first message sent MUST be of type JoinRoomMessage');
                     }
-                } else {
-                    /*if (message.hasJoinroommessage()) {
-                        throw new Error('Cannot call JoinRoomMessage twice!');
-                    } else if (message.hasUsermovesmessage()) {
-                        socketManager.handleUserMovesMessage(room, user, message.getUsermovesmessage() as UserMovesMessage);
-                    } else if (message.hasSilentmessage()) {
-                        socketManager.handleSilentMessage(room, user, message.getSilentmessage() as SilentMessage);
-                    } else if (message.hasItemeventmessage()) {
-                        socketManager.handleItemEvent(room, user, message.getItemeventmessage() as ItemEventMessage);
-                    } else if (message.hasWebrtcsignaltoservermessage()) {
-                        socketManager.emitVideo(room, user, message.getWebrtcsignaltoservermessage() as WebRtcSignalToServerMessage);
-                    } else if (message.hasWebrtcscreensharingsignaltoservermessage()) {
-                        socketManager.emitScreenSharing(room, user, message.getWebrtcscreensharingsignaltoservermessage() as WebRtcSignalToServerMessage);
-                    } else if (message.hasPlayglobalmessage()) {
-                        socketManager.emitPlayGlobalMessage(room, message.getPlayglobalmessage() as PlayGlobalMessage);
-                    } else if (message.hasQueryjitsijwtmessage()){
-                        socketManager.handleQueryJitsiJwtMessage(user, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
-                    } else {
-                        throw new Error('Unhandled message type');
-                    }*/
                 }
             } catch (e) {
                 emitError(call, e);
@@ -208,6 +180,10 @@ const roomManager: IRoomManagerServer = {
 
         callback(null, new EmptyMessage());
     },
+    sendAdminMessageToRoom(call: ServerUnaryCall<AdminRoomMessage>, callback: sendUnaryData<EmptyMessage>): void {
+        socketManager.sendAdminRoomMessage(call.request.getRoomid(), call.request.getMessage());
+        callback(null, new EmptyMessage());
+    },
 };
 
 export {roomManager};
diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts
index 3e2dd3e06e87a3817d53f8599e71f2bbecec390b..ef969a76d753349efc1135df969e7a309c21831b 100644
--- a/back/src/Services/AdminApi.ts
+++ b/back/src/Services/AdminApi.ts
@@ -1,6 +1,5 @@
 import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
 import Axios from "axios";
-import {v4} from "uuid";
 
 export interface AdminApiData {
     organizationSlug: string
@@ -21,13 +20,6 @@ export interface CharacterTexture {
     rights: string
 }
 
-export interface FetchMemberDataByUuidResponse {
-    uuid: string;
-    tags: string[];
-    textures: CharacterTexture[];
-    messages: unknown[];
-}
-
 class AdminApi {
 
     async fetchMapDetails(organizationSlug: string, worldSlug: string, roomSlug: string|undefined): Promise<AdminApiData> {
@@ -52,65 +44,6 @@ class AdminApi {
         )
         return res.data;
     }
-
-    async fetchMemberDataByUuid(uuid: string): Promise<FetchMemberDataByUuidResponse> {
-        if (!ADMIN_API_URL) {
-            return Promise.reject('No admin backoffice set!');
-        }
-        try {
-            const res = await Axios.get(ADMIN_API_URL+'/api/membership/'+uuid,
-                { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} }
-            )
-            return res.data;
-        } catch (e) {
-            if (e?.response?.status == 404) {
-                // If we get an HTTP 404, the token is invalid. Let's perform an anonymous login!
-                console.warn('Cannot find user with uuid "'+uuid+'". Performing an anonymous login instead.');
-                return {
-                    uuid: v4(),
-                    tags: [],
-                    textures: [],
-                    messages: [],
-                }
-            } else {
-                throw e;
-            }
-        }
-    }
-
-    async fetchMemberDataByToken(organizationMemberToken: string): Promise<AdminApiData> {
-        if (!ADMIN_API_URL) {
-            return Promise.reject('No admin backoffice set!');
-        }
-        //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case.
-        const res = await Axios.get(ADMIN_API_URL+'/api/login-url/'+organizationMemberToken,
-            { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} }
-        )
-        return res.data;
-    }
-
-    async fetchCheckUserByToken(organizationMemberToken: string): Promise<AdminApiData> {
-        if (!ADMIN_API_URL) {
-            return Promise.reject('No admin backoffice set!');
-        }
-        //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case.
-        const res = await Axios.get(ADMIN_API_URL+'/api/check-user/'+organizationMemberToken,
-            { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} }
-        )
-        return res.data;
-    }
-
-    reportPlayer(reportedUserUuid: string, reportedUserComment: string, reporterUserUuid: string, reportWorldSlug: string) {
-       return Axios.post(`${ADMIN_API_URL}/api/report`, {
-                reportedUserUuid,
-                reportedUserComment,
-                reporterUserUuid,
-               reportWorldSlug,
-            },
-            {
-                headers: {"Authorization": `${ADMIN_API_TOKEN}`}
-            });
-    }
 }
 
 export const adminApi = new AdminApi();
diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts
index 3d6906eaaa8022e48f41f821b136753af95b797e..907036e15b1ea024ed64fa50415d9a7e0fa042bb 100644
--- a/back/src/Services/SocketManager.ts
+++ b/back/src/Services/SocketManager.ts
@@ -1,5 +1,4 @@
 import {GameRoom} from "../Model/GameRoom";
-import {CharacterLayer} from "_Model/Websocket/CharacterLayer";
 import {
     ItemEventMessage,
     ItemStateMessage,
@@ -22,7 +21,11 @@ import {
     Zone as ProtoZone,
     BatchToPusherMessage,
     SubToPusherMessage,
-    UserJoinedZoneMessage, GroupUpdateZoneMessage, GroupLeftZoneMessage, UserLeftZoneMessage, BanUserMessage
+    UserJoinedZoneMessage,
+    GroupUpdateZoneMessage,
+    GroupLeftZoneMessage,
+    UserLeftZoneMessage,
+    BanUserMessage,
 } from "../Messages/generated/messages_pb";
 import {User, UserSocket} from "../Model/User";
 import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
@@ -51,18 +54,6 @@ import crypto from "crypto";
 
 const debug = Debug('sockermanager');
 
-interface AdminSocketRoomsList {
-    [index: string]: number;
-}
-interface AdminSocketUsersList {
-    [index: string]: boolean;
-}
-
-export interface AdminSocketData {
-    rooms: AdminSocketRoomsList,
-    users: AdminSocketUsersList,
-}
-
 function emitZoneMessage(subMessage: SubToPusherMessage, socket: ZoneSocket): void {
     // TODO: should we batch those every 100ms?
     const batchMessage = new BatchToPusherMessage();
@@ -83,68 +74,13 @@ export class SocketManager {
         });
     }
 
-    /*getAdminSocketDataFor(roomId:string): AdminSocketData {
-        const data:AdminSocketData = {
-            rooms: {},
-            users: {},
-        }
-        const room = this.rooms.get(roomId);
-        if (room === undefined) {
-            return data;
-        }
-        const users = room.getUsers();
-        data.rooms[roomId] = users.size;
-        users.forEach(user => {
-            data.users[user.uuid] = true
-        })
-        return data;
-    }*/
-
     public async handleJoinRoom(socket: UserSocket, joinRoomMessage: JoinRoomMessage): Promise<{ room: GameRoom; user: User }> {
-        /*const positionMessage = joinRoomMessage.getPositionmessage();
-        if (positionMessage === undefined) {
-            // TODO: send error message?
-            throw new Error('Empty pointMessage found in JoinRoomMessage');
-        }*/
-
-        //const position = ProtobufUtils.toPointInterface(positionMessage);
-        //const viewport = client.viewport;
-
-        //this.sockets.set(client.userId, client); //todo: should this be at the end of the function?
 
         //join new previous room
         const {room, user} = await this.joinRoom(socket, joinRoomMessage);
 
-        //const things = room.setViewport(client, viewport);
-
         const roomJoinedMessage = new RoomJoinedMessage();
         roomJoinedMessage.setTagList(joinRoomMessage.getTagList());
-        /*for (const thing of things) {
-            if (thing instanceof User) {
-                const player: ExSocketInterface|undefined = this.sockets.get(thing.id);
-                if (player === undefined) {
-                    console.warn('Something went wrong. The World contains a user "'+thing.id+"' but this user does not exist in the sockets list!");
-                    continue;
-                }
-
-                const userJoinedMessage = new UserJoinedMessage();
-                userJoinedMessage.setUserid(thing.id);
-                userJoinedMessage.setName(player.name);
-                userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(player.characterLayers));
-                userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(player.position));
-
-                roomJoinedMessage.addUser(userJoinedMessage);
-                roomJoinedMessage.setTagList(joinRoomMessage.getTagList());
-            } else if (thing instanceof Group) {
-                const groupUpdateMessage = new GroupUpdateMessage();
-                groupUpdateMessage.setGroupid(thing.getId());
-                groupUpdateMessage.setPosition(ProtobufUtils.toPointMessage(thing.getPosition()));
-
-                roomJoinedMessage.addGroup(groupUpdateMessage);
-            } else {
-                console.error("Unexpected type for Movable returned by setViewport");
-            }
-        }*/
 
         for (const [itemId, item] of room.getItemsState().entries()) {
             const itemStateMessage = new ItemStateMessage();
@@ -158,8 +94,6 @@ export class SocketManager {
 
         const serverToClientMessage = new ServerToClientMessage();
         serverToClientMessage.setRoomjoinedmessage(roomJoinedMessage);
-
-        //user.socket.write(serverToClientMessage);
         console.log('SENDING MESSAGE roomJoinedMessage');
         socket.write(serverToClientMessage);
 
@@ -168,13 +102,6 @@ export class SocketManager {
             user
         };
 
-        /*const serverToClientMessage = new ServerToClientMessage();
-        serverToClientMessage.setRoomjoinedmessage(roomJoinedMessage);
-
-        if (!client.disconnecting) {
-            client.send(serverToClientMessage.serializeBinary().buffer, true);
-        }*/
-
     }
 
     handleUserMovesMessage(room: GameRoom, user: User, userMovesMessage: UserMovesMessage) {
@@ -693,33 +620,6 @@ export class SocketManager {
         }, 10000);
     }
 
-    /**
-     * Merges the characterLayers received from the front (as an array of string) with the custom textures from the back.
-     */
-    static mergeCharacterLayersAndCustomTextures(characterLayers: string[], memberTextures: CharacterTexture[]): CharacterLayer[] {
-        const characterLayerObjs: CharacterLayer[] = [];
-        for (const characterLayer of characterLayers) {
-            if (characterLayer.startsWith('customCharacterTexture')) {
-                const customCharacterLayerId: number = +characterLayer.substr(22);
-                for (const memberTexture of memberTextures) {
-                    if (memberTexture.id == customCharacterLayerId) {
-                        characterLayerObjs.push({
-                            name: characterLayer,
-                            url: memberTexture.url
-                        })
-                        break;
-                    }
-                }
-            } else {
-                characterLayerObjs.push({
-                    name: characterLayer,
-                    url: undefined
-                })
-            }
-        }
-        return characterLayerObjs;
-    }
-
     public addZoneListener(call: ZoneSocket, roomId: string, x: number, y: number): void {
         const room = this.rooms.get(roomId);
         if (!room) {
@@ -773,11 +673,6 @@ export class SocketManager {
     public async handleJoinAdminRoom(admin: Admin, roomId: string): Promise<GameRoom> {
         const room = await socketManager.getOrCreateRoom(roomId);
 
-        // Dispatch groups position to newly connected user
-        /*world.getGroups().forEach((group: Group) => {
-            this.emitCreateUpdateGroupEvent(socket, group);
-        });*/
-
         room.adminJoin(admin);
 
         return room;
@@ -807,7 +702,7 @@ export class SocketManager {
 
         const sendUserMessage = new SendUserMessage();
         sendUserMessage.setMessage(message);
-        sendUserMessage.setType('ban');
+        sendUserMessage.setType('ban'); //todo: is the type correct?
 
         const subToPusherMessage = new SubToPusherMessage();
         subToPusherMessage.setSendusermessage(sendUserMessage);
@@ -843,6 +738,27 @@ export class SocketManager {
         // Let's close the connection when the user is banned.
         recipient.socket.end();
     }
+
+
+    sendAdminRoomMessage(roomId: string, message: string) {
+        const room = this.rooms.get(roomId);
+        if (!room) {
+            //todo: this should cause the http call to return a 500
+            console.error("In sendAdminRoomMessage, could not find room with id '" +  roomId + "'. Maybe the room was closed a few milliseconds ago and there was a race condition?");
+            return;
+        }
+
+        room.getUsers().forEach((recipient) => {
+            const sendUserMessage = new SendUserMessage();
+            sendUserMessage.setMessage(message);
+            sendUserMessage.setType('message');
+
+            const clientMessage = new ServerToClientMessage();
+            clientMessage.setSendusermessage(sendUserMessage);
+
+            recipient.socket.write(clientMessage);
+        });
+    }
 }
 
 export const socketManager = new SocketManager();
diff --git a/front/src/Administration/ConsoleGlobalMessageManager.ts b/front/src/Administration/ConsoleGlobalMessageManager.ts
index 196e9e1cb8caa536226d3c5f54fd77abdbffd33d..7800e33256fd05ca457859c15a590733b671431a 100644
--- a/front/src/Administration/ConsoleGlobalMessageManager.ts
+++ b/front/src/Administration/ConsoleGlobalMessageManager.ts
@@ -3,6 +3,7 @@ import {UserInputManager} from "../Phaser/UserInput/UserInputManager";
 import {RoomConnection} from "../Connexion/RoomConnection";
 import {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
 import {ADMIN_URL} from "../Enum/EnvironmentVariable";
+import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService";
 
 export const CLASS_CONSOLE_MESSAGE = 'main-console';
 export const INPUT_CONSOLE_MESSAGE = 'input-send-text';
@@ -10,13 +11,16 @@ export const UPLOAD_CONSOLE_MESSAGE = 'input-upload-music';
 export const INPUT_TYPE_CONSOLE = 'input-type';
 export const VIDEO_QUALITY_SELECT = 'select-video-quality';
 
-export const AUDIO_TYPE = 'audio';
-export const MESSAGE_TYPE = 'message';
+export const AUDIO_TYPE = AdminMessageEventTypes.audio;
+export const MESSAGE_TYPE = AdminMessageEventTypes.admin;
 
 interface EventTargetFiles extends EventTarget {
     files: Array<File>;
 }
 
+/**
+ * @deprecated
+ */
 export class ConsoleGlobalMessageManager {
 
     private readonly divMainConsole: HTMLDivElement;
@@ -372,23 +376,6 @@ export class ConsoleGlobalMessageManager {
         this.buttonSendMainConsole.classList.remove('active');
     }
 
-    /*activeSettingConsole(){
-        this.activeSetting = true;
-        if(this.activeMessage){
-            this.disabledSettingConsole();
-        }
-        this.active();
-        this.divSettingConsole.classList.add('active');
-        //this.buttonSettingsMainConsole.classList.add('active');
-    }
-
-    disabledSettingConsole(){
-        this.activeSetting = false;
-        this.disabled();
-        this.divSettingConsole.classList.remove('active');
-        //this.buttonSettingsMainConsole.classList.remove('active');
-    }*/
-
     private getSectionId(id: string) : string {
         return `section-${id}`;
     }
diff --git a/front/src/Administration/UserMessageManager.ts b/front/src/Administration/UserMessageManager.ts
index a20b472901c77a4f9d177f85913b24574fd761c8..ec02ac3b5ac889d84cdb399b794d980e4777117d 100644
--- a/front/src/Administration/UserMessageManager.ts
+++ b/front/src/Administration/UserMessageManager.ts
@@ -1,39 +1,29 @@
-import {RoomConnection} from "../Connexion/RoomConnection";
 import * as TypeMessages from "./TypeMessage";
-import List = Phaser.Structs.List;
-import {UpdatedLocalStreamCallback} from "../WebRtc/MediaManager";
 import {Banned} from "./TypeMessage";
+import {adminMessagesService} from "../Connexion/AdminMessagesService";
 
 export interface TypeMessageInterface {
     showMessage(message: string): void;
 }
 
-export class UserMessageManager {
+class UserMessageManager {
 
     typeMessages: Map<string, TypeMessageInterface> = new Map<string, TypeMessageInterface>();
-    receiveBannedMessageListener: Set<Function> = new Set<UpdatedLocalStreamCallback>();
+    receiveBannedMessageListener!: Function;
 
-    constructor(private Connection: RoomConnection) {
+    constructor() {
         const valueTypeMessageTab = Object.values(TypeMessages);
         Object.keys(TypeMessages).forEach((value: string, index: number) => {
             const typeMessageInstance: TypeMessageInterface = (new valueTypeMessageTab[index]() as TypeMessageInterface);
             this.typeMessages.set(value.toLowerCase(), typeMessageInstance);
         });
-        this.initialise();
-    }
-
-    initialise() {
-        //receive signal to show message
-        this.Connection.receiveUserMessage((type: string, message: string) => {
-            const typeMessage = this.showMessage(type, message);
 
-            //listener on banned receive message
+        adminMessagesService.messageStream.subscribe((event) => {
+            const typeMessage = this.showMessage(event.type, event.text);
             if(typeMessage instanceof Banned) {
-                for (const callback of this.receiveBannedMessageListener) {
-                    callback();
-                }
+                this.receiveBannedMessageListener();
             }
-        });
+        })
     }
 
     showMessage(type: string, message: string) {
@@ -47,6 +37,7 @@ export class UserMessageManager {
     }
 
     setReceiveBanListener(callback: Function){
-        this.receiveBannedMessageListener.add(callback);
+        this.receiveBannedMessageListener = callback;
     }
-}
\ No newline at end of file
+}
+export const userMessageManager = new UserMessageManager()
\ No newline at end of file
diff --git a/front/src/Connexion/AdminMessagesService.ts b/front/src/Connexion/AdminMessagesService.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a1e7fc21dd0591ba47fd43334ef0592ac02125e9
--- /dev/null
+++ b/front/src/Connexion/AdminMessagesService.ts
@@ -0,0 +1,34 @@
+import {Subject} from "rxjs";
+import {SendUserMessage} from "../Messages/generated/messages_pb";
+
+export enum AdminMessageEventTypes {
+    admin = 'message',
+    audio = 'audio',
+    ban = 'ban',
+}
+
+interface AdminMessageEvent {
+    type: AdminMessageEventTypes,
+    text: string;
+    //todo add optional properties for other event types
+}
+
+//this class is designed to easily allow communication between the RoomConnection objects (that receive the message)
+//and the various objects that may render the message on screen
+class AdminMessagesService {
+    private _messageStream: Subject<AdminMessageEvent> = new Subject();
+    public messageStream = this._messageStream.asObservable();
+    
+    constructor() {
+        this.messageStream.subscribe((event) => console.log('message', event))
+    }
+    
+    onSendusermessage(message: SendUserMessage) {
+        this._messageStream.next({
+            type: message.getType() as unknown as AdminMessageEventTypes,
+            text: message.getMessage(),
+        })
+    }
+}
+
+export const adminMessagesService = new AdminMessagesService();
\ No newline at end of file
diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts
index cebd7606ec3ff16a837dd033a19e599a937f6a0b..7d48946b054a809e6eca0b46b41ac785a89ca53f 100644
--- a/front/src/Connexion/RoomConnection.ts
+++ b/front/src/Connexion/RoomConnection.ts
@@ -42,6 +42,7 @@ import {
     WebRtcSignalReceivedMessageInterface,
 } from "./ConnexionModels";
 import {BodyResourceDescriptionInterface} from "../Phaser/Entity/PlayerTextures";
+import {adminMessagesService} from "./AdminMessagesService";
 
 const manualPingDelay = 20000;
 
@@ -140,8 +141,6 @@ export class RoomConnection implements RoomConnection {
             } else if (message.hasRoomjoinedmessage()) {
                 const roomJoinedMessage = message.getRoomjoinedmessage() as RoomJoinedMessage;
 
-                //const users: Array<MessageUserJoined> = roomJoinedMessage.getUserList().map(this.toMessageUserJoined.bind(this));
-                //const groups: Array<GroupCreatedUpdatedMessageInterface> = roomJoinedMessage.getGroupList().map(this.toGroupCreatedUpdatedMessage.bind(this));
                 const items: { [itemId: number] : unknown } = {};
                 for (const item of roomJoinedMessage.getItemList()) {
                     items[item.getItemid()] = JSON.parse(item.getStatejson());
@@ -150,22 +149,12 @@ export class RoomConnection implements RoomConnection {
                 this.userId = roomJoinedMessage.getCurrentuserid();
                 this.tags = roomJoinedMessage.getTagList();
 
-                //console.log('Dispatching CONNECT')
                 this.dispatch(EventMessage.CONNECT, {
                     connection: this,
                     room: {
-                        //users,
-                        //groups,
                         items
                     } as RoomJoinedMessageInterface
                 });
-
-                /*console.log('Dispatching START_ROOM')
-                this.dispatch(EventMessage.START_ROOM, {
-                    //users,
-                    //groups,
-                    items
-                });*/
             } else if (message.hasErrormessage()) {
                 console.error(EventMessage.MESSAGE_ERROR, message.getErrormessage()?.getMessage());
             } else if (message.hasWebrtcsignaltoclientmessage()) {
@@ -185,7 +174,7 @@ export class RoomConnection implements RoomConnection {
             } else if (message.hasSendjitsijwtmessage()) {
                 this.dispatch(EventMessage.START_JITSI_ROOM, message.getSendjitsijwtmessage());
             } else if (message.hasSendusermessage()) {
-                this.dispatch(EventMessage.USER_MESSAGE, message.getSendusermessage());
+                adminMessagesService.onSendusermessage(message.getSendusermessage() as SendUserMessage);
             } else {
                 throw new Error('Unknown message received');
             }
@@ -539,12 +528,6 @@ export class RoomConnection implements RoomConnection {
         });
     }
 
-    public receiveUserMessage(callback: (type: string, message: string) => void) {
-        return this.onMessage(EventMessage.USER_MESSAGE, (message: SendUserMessage) => {
-            callback(message.getType(), message.getMessage());
-        });
-    }
-
     public emitGlobalMessage(message: PlayGlobalMessageInterface){
         const playGlobalMessage = new PlayGlobalMessage();
         playGlobalMessage.setId(message.id);
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index a90cb6b189619178e8689dc41775450885ee0c32..cc3378a68ec87ce4ddc61b0e595e6259630e8445 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -57,7 +57,7 @@ import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils";
 import {connectionManager} from "../../Connexion/ConnectionManager";
 import {RoomConnection} from "../../Connexion/RoomConnection";
 import {GlobalMessageManager} from "../../Administration/GlobalMessageManager";
-import {UserMessageManager} from "../../Administration/UserMessageManager";
+import {userMessageManager} from "../../Administration/UserMessageManager";
 import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager";
 import {ResizableScene} from "../Login/ResizableScene";
 import {Room} from "../../Connexion/Room";
@@ -72,7 +72,6 @@ import {TextureError} from "../../Exception/TextureError";
 import {addLoader} from "../Components/Loader";
 import {ErrorSceneName} from "../Reconnecting/ErrorScene";
 import {localUserStore} from "../../Connexion/LocalUserStore";
-import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures";
 
 export interface GameSceneInitInterface {
     initPosition: PointInterface|null,
@@ -131,7 +130,6 @@ export class GameScene extends ResizableScene implements CenterListener {
     public connection!: RoomConnection;
     private simplePeer!: SimplePeer;
     private GlobalMessageManager!: GlobalMessageManager;
-    private UserMessageManager!: UserMessageManager;
     public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager;
     private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>;
     private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>) => void;
@@ -537,8 +535,7 @@ export class GameScene extends ResizableScene implements CenterListener {
             // When connection is performed, let's connect SimplePeer
             this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName);
             this.GlobalMessageManager = new GlobalMessageManager(this.connection);
-            this.UserMessageManager = new UserMessageManager(this.connection);
-            this.UserMessageManager.setReceiveBanListener(this.bannedUser.bind(this));
+            userMessageManager.setReceiveBanListener(this.bannedUser.bind(this));
 
             const self = this;
             this.simplePeer.registerPeerConnectionListener({
diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto
index 54b425f94929f8f515be3ebd39841cb0ccfeee19..36247aefebba33aff709d95a15ebe24514f77c8e 100644
--- a/messages/protos/messages.proto
+++ b/messages/protos/messages.proto
@@ -217,6 +217,7 @@ message ServerToClientMessage {
     SendJitsiJwtMessage sendJitsiJwtMessage = 11;
     SendUserMessage sendUserMessage = 12;
     BanUserMessage banUserMessage = 13;
+    AdminRoomMessage adminRoomMessage = 14;
   }
 }
 
@@ -351,6 +352,12 @@ message AdminMessage {
   string roomId = 3;
 }
 
+// A message sent by an administrator to everyone in a specific room
+message AdminRoomMessage {
+  string message = 1;
+  string roomId = 2;
+}
+
 // A message sent by an administrator to absolutely everybody
 message AdminGlobalMessage {
   string message = 1;
@@ -372,4 +379,5 @@ service RoomManager {
   rpc sendAdminMessage(AdminMessage) returns (EmptyMessage);
   rpc sendGlobalAdminMessage(AdminGlobalMessage) returns (EmptyMessage);
   rpc ban(BanMessage) returns (EmptyMessage);
+  rpc sendAdminMessageToRoom(AdminRoomMessage) returns (EmptyMessage);
 }
diff --git a/pusher/src/App.ts b/pusher/src/App.ts
index 49786d3f306806a1726242b065cbe8105d18bf06..7a272404d908f4b65a5a9eec74990ad3e3404d16 100644
--- a/pusher/src/App.ts
+++ b/pusher/src/App.ts
@@ -5,6 +5,7 @@ import {MapController} from "./Controller/MapController";
 import {PrometheusController} from "./Controller/PrometheusController";
 import {DebugController} from "./Controller/DebugController";
 import {App as uwsApp} from "./Server/sifrr.server";
+import {AdminController} from "./Controller/AdminController";
 
 class App {
     public app: uwsApp;
@@ -13,6 +14,7 @@ class App {
     public mapController: MapController;
     public prometheusController: PrometheusController;
     private debugController: DebugController;
+    private adminController: AdminController;
 
     constructor() {
         this.app = new uwsApp();
@@ -23,6 +25,7 @@ class App {
         this.mapController = new MapController(this.app);
         this.prometheusController = new PrometheusController(this.app);
         this.debugController = new DebugController(this.app);
+        this.adminController = new AdminController(this.app);
     }
 }
 
diff --git a/pusher/src/Controller/AdminController.ts b/pusher/src/Controller/AdminController.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8c4f524db6eb6cda9a764f317c6c6aef155ea885
--- /dev/null
+++ b/pusher/src/Controller/AdminController.ts
@@ -0,0 +1,73 @@
+import {BaseController} from "./BaseController";
+import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js";
+import {ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable";
+import {apiClientRepository} from "../Services/ApiClientRepository";
+import {AdminRoomMessage} from "../Messages/generated/messages_pb";
+
+
+export class AdminController extends BaseController{
+
+    constructor(private App : TemplatedApp) {
+        super();
+        this.App = App;
+        this.receiveGlobalMessagePrompt();
+    }
+    
+    receiveGlobalMessagePrompt() {
+        this.App.options("/message", (res: HttpResponse, req: HttpRequest) => {
+            this.addCorsHeaders(res);
+            res.end();
+        });
+
+        // eslint-disable-next-line @typescript-eslint/no-misused-promises
+        this.App.post("/message", async (res: HttpResponse, req: HttpRequest) => {
+
+            res.onAborted(() => {
+                console.warn('/message request was aborted');
+            })
+
+
+            const token = req.getHeader('admin-token');
+            const body = await res.json();
+           
+            if (token !== ADMIN_API_TOKEN) {
+                console.error('Admin access refused for token: '+token)
+                res.writeStatus("401 Unauthorized").end('Incorrect token');
+                return;
+            }
+
+            try {
+                if (typeof body.text !== 'string') {
+                   throw 'Incorrect text parameter'
+                }
+                if (!body.targets || typeof body.targets !== 'object') {
+                    throw 'Incorrect targets parameter'
+                }
+                const text: string = body.text;
+                const targets: string[] = body.targets;
+
+                await Promise.all(targets.map((roomId) => {
+                    return apiClientRepository.getClient(roomId).then((roomClient) =>{
+                        return new Promise((res, rej) => {
+                            const roomMessage = new AdminRoomMessage();
+                            roomMessage.setMessage(text);
+                            roomMessage.setRoomid(roomId);
+                            
+                            roomClient.sendAdminMessageToRoom(roomMessage, (err) => {
+                                err ? rej(err) : res();
+                            });
+                        });
+                    });
+                }));
+                
+            } catch (err) {
+                this.errorToResponse(err, res);
+                return;
+            }
+
+            res.writeStatus("200");
+            this.addCorsHeaders(res);
+            res.end('ok');
+        });
+    }
+}
diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts
index f82974b664d74523892edcfb0340367b4b4a0e9e..a5aa47cc9715f18eae763a37226764cbfd70f644 100644
--- a/pusher/src/Controller/AuthenticateController.ts
+++ b/pusher/src/Controller/AuthenticateController.ts
@@ -56,6 +56,7 @@ export class AuthenticateController extends BaseController {
                         worldSlug,
                         roomSlug,
                         mapUrlStart,
+                        organizationMemberToken,
                         textures
                     }));
 
diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts
index 6bdbd36d54d28bad1bc3417d839dee7a7d349eab..2391aaa38035515d0413a319f2d9e08fe9aad516 100644
--- a/pusher/src/Controller/IoSocketController.ts
+++ b/pusher/src/Controller/IoSocketController.ts
@@ -21,7 +21,6 @@ import {jwtTokenManager} from "../Services/JWTTokenManager";
 import {adminApi, CharacterTexture, FetchMemberDataByUuidResponse} from "../Services/AdminApi";
 import {SocketManager, socketManager} from "../Services/SocketManager";
 import {emitInBatch} from "../Services/IoSocketHelpers";
-import {clientEventsEmitter} from "../Services/ClientEventsEmitter";
 import {ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER} from "../Enum/EnvironmentVariable";
 import {Zone} from "_Model/Zone";
 import {ExAdminSocketInterface} from "_Model/Websocket/ExAdminSocketInterface";
@@ -65,22 +64,6 @@ export class IoSocketController {
                 ws.disconnecting = false;
 
                 socketManager.handleAdminRoom(ws as ExAdminSocketInterface, ws.roomId as string);
-
-                /*ws.send('Data:'+JSON.stringify(socketManager.getAdminSocketDataFor(ws.roomId as string)));
-                ws.clientJoinCallback = (clientUUid: string, roomId: string) => {
-                    const wsroomId = ws.roomId as string;
-                    if(wsroomId === roomId) {
-                        ws.send('MemberJoin:'+clientUUid+';'+roomId);
-                    }
-                };
-                ws.clientLeaveCallback = (clientUUid: string, roomId: string) => {
-                    const wsroomId = ws.roomId as string;
-                    if(wsroomId === roomId) {
-                        ws.send('MemberLeave:'+clientUUid+';'+roomId);
-                    }
-                };
-                clientEventsEmitter.registerToClientJoin(ws.clientJoinCallback);
-                clientEventsEmitter.registerToClientLeave(ws.clientLeaveCallback);*/
             },
             message: (ws, arrayBuffer, isBinary): void => {
                 try {
@@ -107,7 +90,6 @@ export class IoSocketController {
                 const Client = (ws as ExAdminSocketInterface);
                 try {
                     Client.disconnecting = true;
-                    //leave room
                     socketManager.leaveAdminRoom(Client);
                 } catch (e) {
                     console.error('An error occurred on admin "disconnect"');
@@ -207,8 +189,6 @@ export class IoSocketController {
                                 if (!room.anonymous && room.policyType === GameRoomPolicyTypes.MEMBERS_ONLY_POLICY && userData.anonymous === true) {
                                     throw new Error('No correct member')
                                 }
-
-                                //console.log('access granted for user '+userUuid+' and room '+roomId);
                             } catch (e) {
                                 console.log('access not granted for user '+userUuid+' and room '+roomId);
                                 console.error(e);
diff --git a/pusher/src/Services/ApiClientRepository.ts b/pusher/src/Services/ApiClientRepository.ts
index 251b123a8387603f7b338dc8323f65a254f5ece2..c1c6bd3852be2c7211cab102b9ec5de368bd8295 100644
--- a/pusher/src/Services/ApiClientRepository.ts
+++ b/pusher/src/Services/ApiClientRepository.ts
@@ -13,8 +13,7 @@ const debug = Debug('apiClientRespository');
 class ApiClientRepository {
     private roomManagerClients: RoomManagerClient[] = [];
 
-    public constructor(private apiUrls: string[]) {
-    }
+    public constructor(private apiUrls: string[]) {}
 
     public async getClient(roomId: string): Promise<RoomManagerClient> {
         const array = new Uint32Array(crypto.createHash('md5').update(roomId).digest());
diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts
index 713b8c7d057b05a4456d2a92bb0ba5156c1f2a04..5c1f25ed513a7360ca290bda13a2b669d874a9d8 100644
--- a/pusher/src/Services/SocketManager.ts
+++ b/pusher/src/Services/SocketManager.ts
@@ -493,11 +493,7 @@ export class SocketManager implements ZoneEventListener {
     public getWorlds(): Map<string, PusherRoom> {
         return this.Worlds;
     }
-
-    /**
-     *
-     * @param token
-     */
+    
     searchClientByUuid(uuid: string): ExSocketInterface | null {
         for(const socket of this.sockets.values()){
             if(socket.userUuid === uuid){