diff --git a/src/database/entities/server.rs b/src/database/entities/server.rs
index cdea97461d4c0e707bf515a91c8c05a216ae05cb..4fad9551f0437ea6a4eac3951b630929500bc1e3 100644
--- a/src/database/entities/server.rs
+++ b/src/database/entities/server.rs
@@ -258,4 +258,30 @@ impl Server {
 
         Ok(())
     }
+
+    pub async fn remove_member(&self, id: &str) -> Result<()> {
+        get_collection("server_members")
+            .delete_one(
+                doc! {
+                    "_id": {
+                        "server": &self.id,
+                        "user": &id
+                    }
+                },
+                None,
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "delete_one",
+                with: "server_members",
+            })?;
+        
+        ClientboundNotification::ServerMemberLeave {
+            id: self.id.clone(),
+            user: id.to_string()
+        }
+        .publish(self.id.clone());
+
+        Ok(())
+    }
 }
diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs
index 21c427dc2d212a09954728343cdf765727ea3d1b..6ffd106a3b13e00dabefd9b039e5e8a23398977f 100644
--- a/src/database/entities/user.rs
+++ b/src/database/entities/user.rs
@@ -28,14 +28,14 @@ pub enum RelationshipStatus {
     BlockedOther,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct Relationship {
     #[serde(rename = "_id")]
     pub id: String,
     pub status: RelationshipStatus,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub enum Presence {
     Online,
     Idle,
@@ -43,7 +43,7 @@ pub enum Presence {
     Invisible,
 }
 
-#[derive(Validate, Serialize, Deserialize, Debug)]
+#[derive(Validate, Serialize, Deserialize, Debug, Clone)]
 pub struct UserStatus {
     #[validate(length(min = 1, max = 128))]
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -52,7 +52,7 @@ pub struct UserStatus {
     pub presence: Option<Presence>,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct UserProfile {
     #[serde(skip_serializing_if = "Option::is_none")]
     pub content: Option<String>,
@@ -72,7 +72,7 @@ pub enum Badges {
 impl_op_ex_commutative!(+ |a: &i32, b: &Badges| -> i32 { *a | *b as i32 });
 
 // When changing this struct, update notifications/payload.rs#80
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct User {
     #[serde(rename = "_id")]
     pub id: String,
diff --git a/src/database/permissions/server.rs b/src/database/permissions/server.rs
index 2e73aee82e00b90c624a1af8e2c43ae776fb10ad..276e2483a3c3c0df09d0f1ad3345cbd3b2429464 100644
--- a/src/database/permissions/server.rs
+++ b/src/database/permissions/server.rs
@@ -9,7 +9,8 @@ use std::ops;
 #[repr(u32)]
 pub enum ServerPermission {
     View            = 0b00000000000000000000000000000001, // 1
-    // 2 bits of space
+    ManageMembers   = 0b00000000000000000000000000000010, // 2
+    ManageChannels  = 0b00000000000000000000000000000100, // 4
     ManageServer    = 0b00000000000000000000000000001000, // 8
     // 8 bits of space
     ChangeNickname  = 0b00000000000000000001000000000000, // 4096
@@ -26,6 +27,8 @@ bitfield! {
     pub struct ServerPermissions(MSB0 [u32]);
     u32;
     pub get_view, _: 31;
+    pub get_manage_members, _: 30;
+    pub get_manage_channels, _: 29;
     pub get_manage_server, _: 28;
 
     pub get_change_nickname, _: 19;
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index 4d7ec2551073dc34b238d2ecd3ef92056db54d4f..52fee1317c095714f1569a368db97ad2b1828475 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -11,7 +11,7 @@ use crate::{
     util::result::{Error, Result},
 };
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(tag = "error")]
 pub enum WebSocketError {
     LabelMe,
@@ -29,7 +29,7 @@ pub enum ServerboundNotification {
     EndTyping { channel: String },
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub enum RemoveUserField {
     ProfileContent,
     ProfileBackground,
@@ -37,26 +37,26 @@ pub enum RemoveUserField {
     Avatar,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub enum RemoveChannelField {
     Icon,
     Description,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub enum RemoveServerField {
     Icon,
     Banner,
     Description,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 pub enum RemoveMemberField {
     Nickname,
     Avatar
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(tag = "type")]
 pub enum ClientboundNotification {
     Error(WebSocketError),
@@ -210,7 +210,7 @@ pub async fn prehandle_hook(notification: &ClientboundNotification) -> Result<()
     Ok(())
 }
 
-pub fn posthandle_hook(notification: &ClientboundNotification) {
+pub async fn posthandle_hook(notification: &ClientboundNotification) {
     match &notification {
         ClientboundNotification::ChannelDelete { id } => {
             get_hive().hive.drop_topic(&id).ok();
@@ -218,7 +218,7 @@ pub fn posthandle_hook(notification: &ClientboundNotification) {
         ClientboundNotification::ChannelGroupLeave { id, user } => {
             get_hive()
                 .hive
-                .unsubscribe(&user.to_string(), &id.to_string())
+                .unsubscribe(user, id)
                 .ok();
         }
         ClientboundNotification::ServerDelete { id } => {
@@ -227,14 +227,23 @@ pub fn posthandle_hook(notification: &ClientboundNotification) {
         ClientboundNotification::ServerMemberLeave { id, user } => {
             get_hive()
                 .hive
-                .unsubscribe(&user.to_string(), &id.to_string())
+                .unsubscribe(user, id)
                 .ok();
+
+            if let Ok(server) = Ref::from_unchecked(id.clone()).fetch_server().await {
+                for channel in server.channels {
+                    get_hive()
+                        .hive
+                        .unsubscribe(user, &channel)
+                        .ok();
+                }
+            }
         }
         ClientboundNotification::UserRelationship { id, user, status } => {
             if status == &RelationshipStatus::None {
                 get_hive()
                     .hive
-                    .unsubscribe(&id.to_string(), &user.id.to_string())
+                    .unsubscribe(id, &user.id)
                     .ok();
             }
         }
diff --git a/src/notifications/hive.rs b/src/notifications/hive.rs
index 3541e5308f7bab1344195280d20bf193a73eaf0b..591c6d852037834454cfc049e7a70d34626b134a 100644
--- a/src/notifications/hive.rs
+++ b/src/notifications/hive.rs
@@ -13,8 +13,11 @@ static HIVE: OnceCell<Hive> = OnceCell::new();
 
 pub async fn init_hive() {
     let hive = MongodbPubSub::new(
-        |ids, notification| {
-            super::events::posthandle_hook(&notification);
+        |ids, notification: ClientboundNotification| {
+            let notif = notification.clone();
+            async_std::task::spawn(async move {
+                super::events::posthandle_hook(&notif).await;
+            });
 
             if let Ok(data) = to_string(&notification) {
                 debug!("Pushing out notification. {}", data);
diff --git a/src/routes/servers/member_remove.rs b/src/routes/servers/member_remove.rs
new file mode 100644
index 0000000000000000000000000000000000000000..645dccff65ef83e2e1a2a56da7eb1a1d2f90b044
--- /dev/null
+++ b/src/routes/servers/member_remove.rs
@@ -0,0 +1,25 @@
+use crate::database::*;
+use crate::util::result::{Error, Result};
+
+use mongodb::bson::doc;
+
+#[delete("/<target>/members/<member>")]
+pub async fn req(user: User, target: Ref, member: String) -> Result<()> {
+    let target = target.fetch_server().await?;
+
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_server(&target)
+        .for_server()
+        .await?;
+    
+    if !perm.get_manage_members() {
+        return Err(Error::MissingPermission)
+    }
+
+    let member = Ref::from(member)?.fetch_member(&target.id).await?;
+    if member.id.user == user.id {
+        return Err(Error::InvalidOperation)
+    }
+
+    target.remove_member(&member.id.user).await
+}
diff --git a/src/routes/servers/mod.rs b/src/routes/servers/mod.rs
index 195081e77b580043b2870207a80c772080f987b6..cf867c56fa86709347da5946fcf3f3c630a39e92 100644
--- a/src/routes/servers/mod.rs
+++ b/src/routes/servers/mod.rs
@@ -8,6 +8,7 @@ mod server_edit;
 mod channel_create;
 
 mod member_fetch_all;
+mod member_remove;
 mod member_fetch;
 mod member_edit;
 
@@ -21,6 +22,7 @@ pub fn routes() -> Vec<Route> {
         server_edit::req,
         channel_create::req,
         member_fetch_all::req,
+        member_remove::req,
         member_fetch::req,
         member_edit::req,
         invites_fetch::req