diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs
index e9cbda64bb3956152b2f4f962c3aafe5a745cf3f..dc2869a608d2158b52fa8d0751a997c5ba6a1318 100644
--- a/src/database/entities/channel.rs
+++ b/src/database/entities/channel.rs
@@ -186,6 +186,27 @@ impl Channel {
                 with: "channel",
             })?;
 
+        // Remove from server object.
+        if let Channel::TextChannel { server, .. } = &self {
+            get_collection("servers")
+                .update_one(
+                    doc !{
+                        "_id": server
+                    },
+                    doc! {
+                        "$pull": {
+                            "channels": id
+                        }
+                    },
+                    None,
+                )
+                .await
+                .map_err(|_| Error::DatabaseError {
+                    operation: "update_one",
+                    with: "servers",
+                })?;
+        }
+
         ClientboundNotification::ChannelDelete { id: id.to_string() }.publish(id.to_string());
 
         if let Channel::Group { icon, .. } = self {
diff --git a/src/database/entities/invites.rs b/src/database/entities/invites.rs
index 1371bfbd2217b6854066c1579974093d9ef5997f..7574a178bd3e3f6382d500f031fc1ce432be2197 100644
--- a/src/database/entities/invites.rs
+++ b/src/database/entities/invites.rs
@@ -13,6 +13,7 @@ pub enum Invite {
     Server {
         #[serde(rename = "_id")]
         code: String,
+        server: String,
         creator: String,
         channel: String,
     },
@@ -35,6 +36,13 @@ impl Invite {
         }
     }
 
+    pub fn creator(&self) -> &String {
+        match &self {
+            Invite::Server { creator, .. } => creator,
+            Invite::Group { creator, .. } => creator,
+        }
+    }
+
     pub async fn get(code: &str) -> Result<Invite> {
         let doc = get_collection("invites")
             .find_one(doc! { "_id": code }, None)
diff --git a/src/database/entities/server.rs b/src/database/entities/server.rs
index a8023d607df39de5255eda3c8a5856910b2b3fcc..fefdc0e9ddf53f82e2d0fcd84c89d1c64c932263 100644
--- a/src/database/entities/server.rs
+++ b/src/database/entities/server.rs
@@ -1,9 +1,11 @@
 use crate::database::*;
 use crate::notifications::events::ClientboundNotification;
 use crate::util::result::{Error, Result};
+use mongodb::bson::Document;
 use mongodb::bson::doc;
-use mongodb::bson::from_document;
+use futures::StreamExt;
 use mongodb::bson::to_document;
+use mongodb::options::FindOptions;
 use rocket_contrib::json::JsonValue;
 use serde::{Deserialize, Serialize};
 
@@ -26,7 +28,8 @@ pub struct Member {
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct Ban {
-    pub id: String,
+    #[serde(rename = "_id")]
+    pub id: MemberCompositeKey,
     pub reason: String,
 }
 
@@ -84,7 +87,123 @@ impl Server {
     }
 
     pub async fn delete(&self) -> Result<()> {
-        unimplemented!()
+        let messages = get_collection("messages");
+
+        // Check if there are any attachments we need to delete.
+        let message_ids = messages
+            .find(
+                doc! {
+                    "server": &self.id,
+                    "attachment": {
+                        "$exists": 1
+                    }
+                },
+                FindOptions::builder().projection(doc! { "_id": 1 }).build(),
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "fetch_many",
+                with: "messages",
+            })?
+            .filter_map(async move |s| s.ok())
+            .collect::<Vec<Document>>()
+            .await
+            .into_iter()
+            .filter_map(|x| x.get_str("_id").ok().map(|x| x.to_string()))
+            .collect::<Vec<String>>();
+
+        // If we found any, mark them as deleted.
+        if message_ids.len() > 0 {
+            get_collection("attachments")
+                .update_many(
+                    doc! {
+                        "message_id": {
+                            "$in": message_ids
+                        }
+                    },
+                    doc! {
+                        "$set": {
+                            "deleted": true
+                        }
+                    },
+                    None,
+                )
+                .await
+                .map_err(|_| Error::DatabaseError {
+                    operation: "update_many",
+                    with: "attachments",
+                })?;
+        }
+
+        // And then delete said messages.
+        messages
+            .delete_many(
+                doc! {
+                    "server": &self.id
+                },
+                None,
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "delete_many",
+                with: "messages",
+            })?;
+        
+        // Delete all channels, members, bans and invites.
+        for with in [ "channels", "invites" ] {
+            get_collection(with)
+                .delete_many(
+                    doc! {
+                        "server": &self.id
+                    },
+                    None,
+                )
+                .await
+                .map_err(|_| Error::DatabaseError {
+                    operation: "delete_many",
+                    with,
+                })?;
+        }
+
+        for with in [ "server_members", "server_bans" ] {
+            get_collection(with)
+                .delete_many(
+                    doc! {
+                        "_id.server": &self.id
+                    },
+                    None,
+                )
+                .await
+                .map_err(|_| Error::DatabaseError {
+                    operation: "delete_many",
+                    with,
+                })?;
+        }
+
+        get_collection("servers")
+            .delete_one(
+                doc! {
+                    "_id": &self.id
+                },
+                None,
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "delete_one",
+                with: "server",
+            })?;
+
+        ClientboundNotification::ServerDelete { id: self.id.clone() }.publish(self.id.clone());
+
+        if let Some(attachment) = &self.icon {
+            attachment.delete().await?;
+        }
+
+        if let Some(attachment) = &self.banner {
+            attachment.delete().await?;
+        }
+
+        Ok(())
     }
 
     pub async fn join_member(&self, id: &str) -> Result<()> {
diff --git a/src/database/guards/reference.rs b/src/database/guards/reference.rs
index dda305413f022a080e09d8a9ef0698349457b60d..513042b0202f441cb0ee2272736867dc8017cb54 100644
--- a/src/database/guards/reference.rs
+++ b/src/database/guards/reference.rs
@@ -38,7 +38,7 @@ impl Ref {
                 operation: "find_one",
                 with: &collection,
             })?
-            .ok_or_else(|| Error::UnknownUser)?;
+            .ok_or_else(|| Error::NotFound)?;
 
         Ok(from_document::<T>(doc).map_err(|_| Error::DatabaseError {
             operation: "from_document",
diff --git a/src/routes/channels/delete_channel.rs b/src/routes/channels/delete_channel.rs
index 0fc7f7b7372bb0333ef57aba087a7cacb14d1f61..faf877e65b5b5cd27fbb4cb3d062c9ef3475c435 100644
--- a/src/routes/channels/delete_channel.rs
+++ b/src/routes/channels/delete_channel.rs
@@ -11,6 +11,7 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
         .with_channel(&target)
         .for_channel()
         .await?;
+    
     if !perm.get_view() {
         Err(Error::MissingPermission)?
     }
diff --git a/src/routes/channels/edit_channel.rs b/src/routes/channels/edit_channel.rs
index 5acf4b0c0c9f4097383029defd0651efd8ead66b..ef428182ac94cd289e9ed95ef16440189f8bac0c 100644
--- a/src/routes/channels/edit_channel.rs
+++ b/src/routes/channels/edit_channel.rs
@@ -45,7 +45,8 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
     }
 
     match &target {
-        Channel::Group { id, icon, .. } => {
+        Channel::Group { id, icon, .. } |
+        Channel::TextChannel { id, icon, .. } => {
             let mut set = doc! {};
             let mut unset = doc! {};
 
@@ -107,42 +108,44 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
             }
             .publish(id.clone());
 
-            if let Some(name) = data.name {
-                Message::create(
-                    "00000000000000000000000000".to_string(),
-                    id.clone(),
-                    Content::SystemMessage(SystemMessage::ChannelRenamed {
-                        name,
-                        by: user.id.clone(),
-                    }),
-                )
-                .publish(&target)
-                .await
-                .ok();
-            }
+            if let Channel::Group { .. } = &target {
+                if let Some(name) = data.name {
+                    Message::create(
+                        "00000000000000000000000000".to_string(),
+                        id.clone(),
+                        Content::SystemMessage(SystemMessage::ChannelRenamed {
+                            name,
+                            by: user.id.clone(),
+                        }),
+                    )
+                    .publish(&target)
+                    .await
+                    .ok();
+                }
 
-            if let Some(_) = data.description {
-                Message::create(
-                    "00000000000000000000000000".to_string(),
-                    id.clone(),
-                    Content::SystemMessage(SystemMessage::ChannelDescriptionChanged {
-                        by: user.id.clone(),
-                    }),
-                )
-                .publish(&target)
-                .await
-                .ok();
-            }
+                if let Some(_) = data.description {
+                    Message::create(
+                        "00000000000000000000000000".to_string(),
+                        id.clone(),
+                        Content::SystemMessage(SystemMessage::ChannelDescriptionChanged {
+                            by: user.id.clone(),
+                        }),
+                    )
+                    .publish(&target)
+                    .await
+                    .ok();
+                }
 
-            if let Some(_) = data.icon {
-                Message::create(
-                    "00000000000000000000000000".to_string(),
-                    id.clone(),
-                    Content::SystemMessage(SystemMessage::ChannelIconChanged { by: user.id }),
-                )
-                .publish(&target)
-                .await
-                .ok();
+                if let Some(_) = data.icon {
+                    Message::create(
+                        "00000000000000000000000000".to_string(),
+                        id.clone(),
+                        Content::SystemMessage(SystemMessage::ChannelIconChanged { by: user.id }),
+                    )
+                    .publish(&target)
+                    .await
+                    .ok();
+                }
             }
 
             if remove_icon {
diff --git a/src/routes/channels/fetch_channel.rs b/src/routes/channels/fetch_channel.rs
index 75a7d5b0229dbb23f4bc8157102c81137e60e04e..cacb983c5dbb91f89c11bf630b7e783a7220e18f 100644
--- a/src/routes/channels/fetch_channel.rs
+++ b/src/routes/channels/fetch_channel.rs
@@ -11,6 +11,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
         .with_channel(&target)
         .for_channel()
         .await?;
+
     if !perm.get_view() {
         Err(Error::MissingPermission)?
     }
diff --git a/src/routes/channels/invite_channel.rs b/src/routes/channels/invite_create.rs
similarity index 91%
rename from src/routes/channels/invite_channel.rs
rename to src/routes/channels/invite_create.rs
index 72001947a8daa7d832c9c341e19a0ce2e2124117..eaef59512610a9e5c8b4340ca72ade0c2b6e3021 100644
--- a/src/routes/channels/invite_channel.rs
+++ b/src/routes/channels/invite_create.rs
@@ -13,7 +13,7 @@ lazy_static! {
     ];
 }
 
-#[post("/<target>/invite")]
+#[post("/<target>/invites")]
 pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
     let target = target.fetch_channel().await?;
     let perm = permissions::PermissionCalculator::new(&user)
@@ -30,10 +30,11 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
         Channel::Group { .. } => {
             unimplemented!()
         }
-        Channel::TextChannel { id, .. } => {
+        Channel::TextChannel { id, server, .. } => {
             Invite::Server {
                 code: code.clone(),
                 creator: user.id,
+                server: server.clone(),
                 channel: id.clone(),
             }
             .save()
diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs
index f8b9be60582e4964926ae9c60671a2de809b98f8..cb7d0b4eea31e29bff3b67e403589808d3b27a44 100644
--- a/src/routes/channels/mod.rs
+++ b/src/routes/channels/mod.rs
@@ -7,7 +7,7 @@ mod fetch_members;
 mod group_add_member;
 mod group_create;
 mod group_remove_member;
-mod invite_channel;
+mod invite_create;
 mod join_call;
 mod message_delete;
 mod message_edit;
@@ -22,7 +22,7 @@ pub fn routes() -> Vec<Route> {
         fetch_members::req,
         delete_channel::req,
         edit_channel::req,
-        invite_channel::req,
+        invite_create::req,
         message_send::req,
         message_query::req,
         message_query_stale::req,
diff --git a/src/routes/invites/invite_delete.rs b/src/routes/invites/invite_delete.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ac3ea1227d7b3038e969465d44496840840cd8b5
--- /dev/null
+++ b/src/routes/invites/invite_delete.rs
@@ -0,0 +1,28 @@
+use crate::database::*;
+use crate::util::result::{Error, Result};
+
+#[delete("/<target>")]
+pub async fn req(user: User, target: Ref) -> Result<()> {
+    let target = target.fetch_invite().await?;
+
+    if target.creator() == &user.id {
+        target.delete().await
+    } else {
+        match &target {
+            Invite::Server { server, .. } => {
+                let server = Ref::from_unchecked(server.clone()).fetch_server().await?;
+                let perm = permissions::PermissionCalculator::new(&user)
+                    .with_server(&server)
+                    .for_server()
+                    .await?;
+
+                if !perm.get_manage_server() {
+                    return Err(Error::MissingPermission);
+                }
+
+                target.delete().await
+            },
+            _ => unreachable!()
+        }
+    }
+}
diff --git a/src/routes/invites/invite_join.rs b/src/routes/invites/invite_join.rs
index ab1b9141fc8e3dbeebcd95fbd364b8ea2376d552..cf7a601c6455bac5a95305138df63917a660caf4 100644
--- a/src/routes/invites/invite_join.rs
+++ b/src/routes/invites/invite_join.rs
@@ -22,6 +22,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
             server.join_member(&user.id).await?;
 
             Ok(json!({
+                "type": "Server",
                 "channel": channel,
                 "server": server
             }))
diff --git a/src/routes/invites/mod.rs b/src/routes/invites/mod.rs
index 426a0f27b468837cef0d1bdb7c345611c5616002..69843215b2699cd52b32dfc325a9f35ee840b1a1 100644
--- a/src/routes/invites/mod.rs
+++ b/src/routes/invites/mod.rs
@@ -1,8 +1,9 @@
 use rocket::Route;
 
+mod invite_delete;
 mod invite_fetch;
 mod invite_join;
 
 pub fn routes() -> Vec<Route> {
-    routes![invite_fetch::req, invite_join::req]
+    routes![invite_fetch::req, invite_join::req, invite_delete::req]
 }
diff --git a/src/routes/servers/mod.rs b/src/routes/servers/mod.rs
index 2b8d5c8bcde48265604db69bee8721ba5c9836bf..20ef08b0aa43dd86a99ef77d2dd1fd55139dae0e 100644
--- a/src/routes/servers/mod.rs
+++ b/src/routes/servers/mod.rs
@@ -2,6 +2,7 @@ use rocket::Route;
 
 mod server_create;
 mod server_delete;
+mod server_fetch;
 mod server_edit;
 
 mod channel_create;
@@ -10,6 +11,7 @@ pub fn routes() -> Vec<Route> {
     routes![
         server_create::req,
         server_delete::req,
+        server_fetch::req,
         server_edit::req,
         channel_create::req
     ]
diff --git a/src/routes/servers/server_delete.rs b/src/routes/servers/server_delete.rs
index 970dc58e7d0dd5e047d6c62145019121150e62e5..29f13afcc7ccfbae130fd6945e7903971c6d7ef6 100644
--- a/src/routes/servers/server_delete.rs
+++ b/src/routes/servers/server_delete.rs
@@ -1,26 +1,42 @@
 use crate::database::*;
 use crate::util::result::{Error, Result};
+use crate::notifications::events::ClientboundNotification;
 
 use mongodb::bson::doc;
 
 #[delete("/<target>")]
 pub async fn req(user: User, target: Ref) -> Result<()> {
     let target = target.fetch_server().await?;
-
-    /*let perm = permissions::PermissionCalculator::new(&user)
-        .with_channel(&target)
-        .for_channel()
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_server(&target)
+        .for_server()
         .await?;
+
     if !perm.get_view() {
-        Err(Error::MissingPermission)?
-    }*/
+        return Err(Error::MissingPermission);
+    }
+
+    if user.id == target.owner {
+        target.delete().await
+    } else {
+        get_collection("server_members")
+            .delete_one(
+                doc! {
 
-    // ! FIXME: either delete server if owner
-    // ! OR leave server if member
+                },
+                None
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "delete_one",
+                with: "server_member"
+            })?;
 
-    // also need to delete server invites
-    // and members
-    // and bans
+        ClientboundNotification::ServerMemberLeave {
+            id: target.id.clone(),
+            user: user.id
+        }.publish(target.id);
 
-    target.delete().await
+        Ok(())
+    }
 }
diff --git a/src/routes/servers/server_fetch.rs b/src/routes/servers/server_fetch.rs
new file mode 100644
index 0000000000000000000000000000000000000000..465b2ef0c6d9a9e61838459242cf4503bf39f34a
--- /dev/null
+++ b/src/routes/servers/server_fetch.rs
@@ -0,0 +1,20 @@
+use crate::database::*;
+use crate::util::result::{Error, Result};
+
+use rocket_contrib::json::JsonValue;
+
+#[get("/<target>")]
+pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
+    let target = target.fetch_server().await?;
+
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_server(&target)
+        .for_server()
+        .await?;
+    
+    if !perm.get_view() {
+        Err(Error::MissingPermission)?
+    }
+
+    Ok(json!(target))
+}
diff --git a/src/util/result.rs b/src/util/result.rs
index 922245b8c94f806d8f235963657fe4a0c6229f10..44d4bad66ec8ceebdb7cf61aa011c1e0c92d147d 100644
--- a/src/util/result.rs
+++ b/src/util/result.rs
@@ -38,7 +38,7 @@ pub enum Error {
 
     // ? Server related errors.
     UnknownServer,
-
+    
     // ? General errors.
     TooManyIds,
     FailedValidation {
@@ -54,6 +54,7 @@ pub enum Error {
     InvalidCredentials,
     DuplicateNonce,
     VosoUnavailable,
+    NotFound,
     NoEffect,
 }
 
@@ -96,6 +97,7 @@ impl<'r> Responder<'r, 'static> for Error {
             Error::InvalidCredentials => Status::Forbidden,
             Error::DuplicateNonce => Status::Conflict,
             Error::VosoUnavailable => Status::BadRequest,
+            Error::NotFound => Status::NotFound,
             Error::NoEffect => Status::Ok,
         };