diff --git a/Cargo.lock b/Cargo.lock
index c48fdea6ad6f9ddc821ce4e4eec094d1c6289f52..8dba0bfa1efbf300f55089a8f4daeb0564347683 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2101,12 +2101,11 @@ dependencies = [
 
 [[package]]
 name = "ruma"
-version = "0.4.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+version = "0.5.0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "assign",
  "js_int",
- "ruma-api",
  "ruma-appservice-api",
  "ruma-client-api",
  "ruma-common",
@@ -2120,40 +2119,11 @@ dependencies = [
  "ruma-state-res",
 ]
 
-[[package]]
-name = "ruma-api"
-version = "0.20.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
-dependencies = [
- "bytes",
- "http",
- "percent-encoding",
- "ruma-api-macros",
- "ruma-identifiers",
- "ruma-serde",
- "serde",
- "serde_json",
- "thiserror",
- "tracing",
-]
-
-[[package]]
-name = "ruma-api-macros"
-version = "0.20.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
-dependencies = [
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "ruma-appservice-api"
-version = "0.4.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+version = "0.5.0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
- "ruma-api",
  "ruma-common",
  "ruma-events",
  "ruma-identifiers",
@@ -2165,7 +2135,7 @@ dependencies = [
 [[package]]
 name = "ruma-client-api"
 version = "0.13.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "assign",
  "bytes",
@@ -2173,7 +2143,6 @@ dependencies = [
  "js_int",
  "maplit",
  "percent-encoding",
- "ruma-api",
  "ruma-common",
  "ruma-events",
  "ruma-identifiers",
@@ -2185,14 +2154,19 @@ dependencies = [
 [[package]]
 name = "ruma-common"
 version = "0.8.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
+ "bytes",
+ "http",
  "indexmap",
  "js_int",
+ "percent-encoding",
  "ruma-identifiers",
+ "ruma-macros",
  "ruma-serde",
  "serde",
  "serde_json",
+ "thiserror",
  "tracing",
  "wildmatch",
 ]
@@ -2200,13 +2174,13 @@ dependencies = [
 [[package]]
 name = "ruma-events"
 version = "0.26.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "indoc",
  "js_int",
  "ruma-common",
- "ruma-events-macros",
  "ruma-identifiers",
+ "ruma-macros",
  "ruma-serde",
  "serde",
  "serde_json",
@@ -2214,24 +2188,12 @@ dependencies = [
  "wildmatch",
 ]
 
-[[package]]
-name = "ruma-events-macros"
-version = "0.26.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
-dependencies = [
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "ruma-federation-api"
-version = "0.3.1"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+version = "0.4.0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "js_int",
- "ruma-api",
  "ruma-common",
  "ruma-events",
  "ruma-identifiers",
@@ -2243,33 +2205,22 @@ dependencies = [
 [[package]]
 name = "ruma-identifiers"
 version = "0.22.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "percent-encoding",
  "rand 0.8.4",
- "ruma-identifiers-macros",
  "ruma-identifiers-validation",
+ "ruma-macros",
  "ruma-serde",
- "ruma-serde-macros",
  "serde",
  "url",
  "uuid",
 ]
 
-[[package]]
-name = "ruma-identifiers-macros"
-version = "0.22.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
-dependencies = [
- "quote",
- "ruma-identifiers-validation",
- "syn",
-]
-
 [[package]]
 name = "ruma-identifiers-validation"
 version = "0.7.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "thiserror",
  "url",
@@ -2277,24 +2228,34 @@ dependencies = [
 
 [[package]]
 name = "ruma-identity-service-api"
-version = "0.3.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+version = "0.4.0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "js_int",
- "ruma-api",
  "ruma-common",
  "ruma-identifiers",
  "ruma-serde",
  "serde",
 ]
 
+[[package]]
+name = "ruma-macros"
+version = "0.1.0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "ruma-identifiers-validation",
+ "syn",
+]
+
 [[package]]
 name = "ruma-push-gateway-api"
-version = "0.3.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+version = "0.4.0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "js_int",
- "ruma-api",
  "ruma-common",
  "ruma-events",
  "ruma-identifiers",
@@ -2306,33 +2267,22 @@ dependencies = [
 [[package]]
 name = "ruma-serde"
 version = "0.6.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "base64 0.13.0",
  "bytes",
  "form_urlencoded",
  "itoa 1.0.1",
  "js_int",
- "ruma-serde-macros",
+ "ruma-macros",
  "serde",
  "serde_json",
 ]
 
-[[package]]
-name = "ruma-serde-macros"
-version = "0.6.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
-dependencies = [
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "ruma-signatures"
 version = "0.10.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "base64 0.13.0",
  "ed25519-dalek",
@@ -2348,8 +2298,8 @@ dependencies = [
 
 [[package]]
 name = "ruma-state-res"
-version = "0.5.0"
-source = "git+https://github.com/ruma/ruma?rev=95fdb303c82e257eee18f5064b87ed4e2ed01ac0#95fdb303c82e257eee18f5064b87ed4e2ed01ac0"
+version = "0.6.0"
+source = "git+https://github.com/ruma/ruma?rev=588fe9c006eb140264160e68f4a21ea1fb28af18#588fe9c006eb140264160e68f4a21ea1fb28af18"
 dependencies = [
  "itertools",
  "js_int",
diff --git a/Cargo.toml b/Cargo.toml
index 67c05536002e3a9a3658825e2407e1380a329ce8..17f158d71e2ff45cd837fe1a1ba0fc6f6ddad8f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,7 +21,7 @@ tower-http = { version = "0.2.1", features = ["add-extension", "cors", "compress
 
 # Used for matrix spec type definitions and helpers
 #ruma = { version = "0.4.0", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
-ruma = { git = "https://github.com/ruma/ruma", rev = "95fdb303c82e257eee18f5064b87ed4e2ed01ac0", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-pre-spec", "unstable-exhaustive-types"] }
+ruma = { git = "https://github.com/ruma/ruma", rev = "588fe9c006eb140264160e68f4a21ea1fb28af18", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-pre-spec", "unstable-exhaustive-types"] }
 #ruma = { git = "https://github.com/timokoesters/ruma", rev = "50c1db7e0a3a21fc794b0cce3b64285a4c750c71", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
 #ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
 
diff --git a/src/client_server/account.rs b/src/client_server/account.rs
index 32488f222dca4740ba69eefb177e94b32f4b09e8..4c2dff9f6121f0506f694aba6027871449db231c 100644
--- a/src/client_server/account.rs
+++ b/src/client_server/account.rs
@@ -342,8 +342,12 @@ pub async fn change_password_route(
 /// Note: Also works for Application Services
 pub async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
+    let device_id = body.sender_device.as_ref().cloned();
+    let is_guest = device_id.is_none();
     Ok(whoami::v3::Response {
         user_id: sender_user.clone(),
+        device_id,
+        is_guest,
     })
 }
 
diff --git a/src/client_server/backup.rs b/src/client_server/backup.rs
index 808d886843e466a5422052f89fd2c0ad99707601..2e449d107caf5e861568f176eedf9b70dedab76c 100644
--- a/src/client_server/backup.rs
+++ b/src/client_server/backup.rs
@@ -1,10 +1,11 @@
 use crate::{database::DatabaseGuard, Error, Result, Ruma};
 use ruma::api::client::{
     backup::{
-        add_backup_key_session, add_backup_key_sessions, add_backup_keys, create_backup,
-        delete_backup, delete_backup_key_session, delete_backup_key_sessions, delete_backup_keys,
-        get_backup, get_backup_key_session, get_backup_key_sessions, get_backup_keys,
-        get_latest_backup, update_backup,
+        add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session,
+        create_backup_version, delete_backup_keys, delete_backup_keys_for_room,
+        delete_backup_keys_for_session, delete_backup_version, get_backup_info, get_backup_keys,
+        get_backup_keys_for_room, get_backup_keys_for_session, get_latest_backup_info,
+        update_backup_version,
     },
     error::ErrorKind,
 };
@@ -14,8 +15,8 @@
 /// Creates a new backup.
 pub async fn create_backup_route(
     db: DatabaseGuard,
-    body: Ruma<create_backup::v3::Request>,
-) -> Result<create_backup::v3::Response> {
+    body: Ruma<create_backup_version::v3::Request>,
+) -> Result<create_backup_version::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
     let version = db
         .key_backups
@@ -23,7 +24,7 @@ pub async fn create_backup_route(
 
     db.flush()?;
 
-    Ok(create_backup::v3::Response { version })
+    Ok(create_backup_version::v3::Response { version })
 }
 
 /// # `PUT /_matrix/client/r0/room_keys/version/{version}`
@@ -31,15 +32,15 @@ pub async fn create_backup_route(
 /// Update information about an existing backup. Only `auth_data` can be modified.
 pub async fn update_backup_route(
     db: DatabaseGuard,
-    body: Ruma<update_backup::v3::Request<'_>>,
-) -> Result<update_backup::v3::Response> {
+    body: Ruma<update_backup_version::v3::Request<'_>>,
+) -> Result<update_backup_version::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
     db.key_backups
         .update_backup(sender_user, &body.version, &body.algorithm, &db.globals)?;
 
     db.flush()?;
 
-    Ok(update_backup::v3::Response {})
+    Ok(update_backup_version::v3::Response {})
 }
 
 /// # `GET /_matrix/client/r0/room_keys/version`
@@ -47,8 +48,8 @@ pub async fn update_backup_route(
 /// Get information about the latest backup version.
 pub async fn get_latest_backup_route(
     db: DatabaseGuard,
-    body: Ruma<get_latest_backup::v3::Request>,
-) -> Result<get_latest_backup::v3::Response> {
+    body: Ruma<get_latest_backup_info::v3::Request>,
+) -> Result<get_latest_backup_info::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
     let (version, algorithm) =
@@ -59,7 +60,7 @@ pub async fn get_latest_backup_route(
                 "Key backup does not exist.",
             ))?;
 
-    Ok(get_latest_backup::v3::Response {
+    Ok(get_latest_backup_info::v3::Response {
         algorithm,
         count: (db.key_backups.count_keys(sender_user, &version)? as u32).into(),
         etag: db.key_backups.get_etag(sender_user, &version)?,
@@ -72,8 +73,8 @@ pub async fn get_latest_backup_route(
 /// Get information about an existing backup.
 pub async fn get_backup_route(
     db: DatabaseGuard,
-    body: Ruma<get_backup::v3::Request<'_>>,
-) -> Result<get_backup::v3::Response> {
+    body: Ruma<get_backup_info::v3::Request<'_>>,
+) -> Result<get_backup_info::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
     let algorithm = db
         .key_backups
@@ -83,7 +84,7 @@ pub async fn get_backup_route(
             "Key backup does not exist.",
         ))?;
 
-    Ok(get_backup::v3::Response {
+    Ok(get_backup_info::v3::Response {
         algorithm,
         count: (db.key_backups.count_keys(sender_user, &body.version)? as u32).into(),
         etag: db.key_backups.get_etag(sender_user, &body.version)?,
@@ -98,15 +99,15 @@ pub async fn get_backup_route(
 /// - Deletes both information about the backup, as well as all key data related to the backup
 pub async fn delete_backup_route(
     db: DatabaseGuard,
-    body: Ruma<delete_backup::v3::Request<'_>>,
-) -> Result<delete_backup::v3::Response> {
+    body: Ruma<delete_backup_version::v3::Request<'_>>,
+) -> Result<delete_backup_version::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
     db.key_backups.delete_backup(sender_user, &body.version)?;
 
     db.flush()?;
 
-    Ok(delete_backup::v3::Response {})
+    Ok(delete_backup_version::v3::Response {})
 }
 
 /// # `PUT /_matrix/client/r0/room_keys/keys`
@@ -164,8 +165,8 @@ pub async fn add_backup_keys_route(
 /// - Returns the new number of keys in this backup and the etag
 pub async fn add_backup_key_sessions_route(
     db: DatabaseGuard,
-    body: Ruma<add_backup_key_sessions::v3::Request<'_>>,
-) -> Result<add_backup_key_sessions::v3::Response> {
+    body: Ruma<add_backup_keys_for_room::v3::Request<'_>>,
+) -> Result<add_backup_keys_for_room::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
     if Some(&body.version)
@@ -193,7 +194,7 @@ pub async fn add_backup_key_sessions_route(
 
     db.flush()?;
 
-    Ok(add_backup_key_sessions::v3::Response {
+    Ok(add_backup_keys_for_room::v3::Response {
         count: (db.key_backups.count_keys(sender_user, &body.version)? as u32).into(),
         etag: db.key_backups.get_etag(sender_user, &body.version)?,
     })
@@ -208,8 +209,8 @@ pub async fn add_backup_key_sessions_route(
 /// - Returns the new number of keys in this backup and the etag
 pub async fn add_backup_key_session_route(
     db: DatabaseGuard,
-    body: Ruma<add_backup_key_session::v3::Request<'_>>,
-) -> Result<add_backup_key_session::v3::Response> {
+    body: Ruma<add_backup_keys_for_session::v3::Request<'_>>,
+) -> Result<add_backup_keys_for_session::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
     if Some(&body.version)
@@ -235,7 +236,7 @@ pub async fn add_backup_key_session_route(
 
     db.flush()?;
 
-    Ok(add_backup_key_session::v3::Response {
+    Ok(add_backup_keys_for_session::v3::Response {
         count: (db.key_backups.count_keys(sender_user, &body.version)? as u32).into(),
         etag: db.key_backups.get_etag(sender_user, &body.version)?,
     })
@@ -260,15 +261,15 @@ pub async fn get_backup_keys_route(
 /// Retrieves all keys from the backup for a given room.
 pub async fn get_backup_key_sessions_route(
     db: DatabaseGuard,
-    body: Ruma<get_backup_key_sessions::v3::Request<'_>>,
-) -> Result<get_backup_key_sessions::v3::Response> {
+    body: Ruma<get_backup_keys_for_room::v3::Request<'_>>,
+) -> Result<get_backup_keys_for_room::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
     let sessions = db
         .key_backups
         .get_room(sender_user, &body.version, &body.room_id)?;
 
-    Ok(get_backup_key_sessions::v3::Response { sessions })
+    Ok(get_backup_keys_for_room::v3::Response { sessions })
 }
 
 /// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
@@ -276,8 +277,8 @@ pub async fn get_backup_key_sessions_route(
 /// Retrieves a key from the backup.
 pub async fn get_backup_key_session_route(
     db: DatabaseGuard,
-    body: Ruma<get_backup_key_session::v3::Request<'_>>,
-) -> Result<get_backup_key_session::v3::Response> {
+    body: Ruma<get_backup_keys_for_session::v3::Request<'_>>,
+) -> Result<get_backup_keys_for_session::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
     let key_data = db
@@ -288,7 +289,7 @@ pub async fn get_backup_key_session_route(
             "Backup key not found for this user's session.",
         ))?;
 
-    Ok(get_backup_key_session::v3::Response { key_data })
+    Ok(get_backup_keys_for_session::v3::Response { key_data })
 }
 
 /// # `DELETE /_matrix/client/r0/room_keys/keys`
@@ -315,8 +316,8 @@ pub async fn delete_backup_keys_route(
 /// Delete the keys from the backup for a given room.
 pub async fn delete_backup_key_sessions_route(
     db: DatabaseGuard,
-    body: Ruma<delete_backup_key_sessions::v3::Request<'_>>,
-) -> Result<delete_backup_key_sessions::v3::Response> {
+    body: Ruma<delete_backup_keys_for_room::v3::Request<'_>>,
+) -> Result<delete_backup_keys_for_room::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
     db.key_backups
@@ -324,7 +325,7 @@ pub async fn delete_backup_key_sessions_route(
 
     db.flush()?;
 
-    Ok(delete_backup_key_sessions::v3::Response {
+    Ok(delete_backup_keys_for_room::v3::Response {
         count: (db.key_backups.count_keys(sender_user, &body.version)? as u32).into(),
         etag: db.key_backups.get_etag(sender_user, &body.version)?,
     })
@@ -335,8 +336,8 @@ pub async fn delete_backup_key_sessions_route(
 /// Delete a key from the backup.
 pub async fn delete_backup_key_session_route(
     db: DatabaseGuard,
-    body: Ruma<delete_backup_key_session::v3::Request<'_>>,
-) -> Result<delete_backup_key_session::v3::Response> {
+    body: Ruma<delete_backup_keys_for_session::v3::Request<'_>>,
+) -> Result<delete_backup_keys_for_session::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
     db.key_backups
@@ -344,7 +345,7 @@ pub async fn delete_backup_key_session_route(
 
     db.flush()?;
 
-    Ok(delete_backup_key_session::v3::Response {
+    Ok(delete_backup_keys_for_session::v3::Response {
         count: (db.key_backups.count_keys(sender_user, &body.version)? as u32).into(),
         etag: db.key_backups.get_etag(sender_user, &body.version)?,
     })
diff --git a/src/client_server/config.rs b/src/client_server/config.rs
index a9a2fb142058670652f8921b397dd3e2fb0785b3..d39f8b6932b3e45175898c63cb2c7f975e7b6339 100644
--- a/src/client_server/config.rs
+++ b/src/client_server/config.rs
@@ -22,7 +22,7 @@ pub async fn set_global_account_data_route(
 ) -> Result<set_global_account_data::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
-    let data: serde_json::Value = serde_json::from_str(body.data.get())
+    let data: serde_json::Value = serde_json::from_str(body.data.json().get())
         .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
 
     let event_type = body.event_type.to_string();
@@ -52,7 +52,7 @@ pub async fn set_room_account_data_route(
 ) -> Result<set_room_account_data::v3::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
 
-    let data: serde_json::Value = serde_json::from_str(body.data.get())
+    let data: serde_json::Value = serde_json::from_str(body.data.json().get())
         .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?;
 
     let event_type = body.event_type.to_string();
diff --git a/src/client_server/directory.rs b/src/client_server/directory.rs
index ad88254e4f77d5bc189fa562dbf1588dfb3fe5a4..f26df8793efe3971bb3957f85a5542e9bb511e60 100644
--- a/src/client_server/directory.rs
+++ b/src/client_server/directory.rs
@@ -11,13 +11,17 @@
         },
         federation,
     },
-    directory::{Filter, IncomingFilter, IncomingRoomNetwork, PublicRoomsChunk, RoomNetwork},
+    directory::{
+        Filter, IncomingFilter, IncomingRoomNetwork, PublicRoomJoinRule, PublicRoomsChunk,
+        RoomNetwork,
+    },
     events::{
         room::{
             avatar::RoomAvatarEventContent,
             canonical_alias::RoomCanonicalAliasEventContent,
             guest_access::{GuestAccess, RoomGuestAccessEventContent},
             history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
+            join_rules::{JoinRule, RoomJoinRulesEventContent},
             name::RoomNameEventContent,
             topic::RoomTopicEventContent,
         },
@@ -265,6 +269,25 @@ pub(crate) async fn get_public_rooms_filtered_helper(
                     .transpose()?
                     // url is now an Option<String> so we must flatten
                     .flatten(),
+                join_rule: db
+                    .rooms
+                    .room_state_get(&room_id, &EventType::RoomJoinRules, "")?
+                    .map(|s| {
+                        serde_json::from_str(s.content.get())
+                            .map(|c: RoomJoinRulesEventContent| match c.join_rule {
+                                JoinRule::Public => Some(PublicRoomJoinRule::Public),
+                                JoinRule::Knock => Some(PublicRoomJoinRule::Knock),
+                                _ => None,
+                            })
+                            .map_err(|_| {
+                                Error::bad_database("Invalid room join rule event in database.")
+                            })
+                    })
+                    .transpose()?
+                    .flatten()
+                    .ok_or(Error::bad_database(
+                        "Invalid room join rule event in database.",
+                    ))?,
                 room_id,
             };
             Ok(chunk)
diff --git a/src/client_server/report.rs b/src/client_server/report.rs
index 1e47792eb55316e44eaf89db8b013befc5837b71..8c51e9ca6b1e0a1032c0de2d0825801f4151f9d4 100644
--- a/src/client_server/report.rs
+++ b/src/client_server/report.rs
@@ -25,14 +25,14 @@ pub async fn report_event_route(
         }
     };
 
-    if body.score > int!(0) || body.score < int!(-100) {
+    if let Some(true) = body.score.map(|s| s > int!(0) || s < int!(-100)) {
         return Err(Error::BadRequest(
             ErrorKind::InvalidParam,
             "Invalid score, must be within 0 to -100",
         ));
     };
 
-    if body.reason.chars().count() > 250 {
+    if let Some(true) = body.reason.clone().map(|s| s.chars().count() > 250) {
         return Err(Error::BadRequest(
             ErrorKind::InvalidParam,
             "Reason too long, should be 250 characters or fewer",
@@ -43,26 +43,26 @@ pub async fn report_event_route(
         .send_message(message::RoomMessageEventContent::text_html(
             format!(
                 "Report received from: {}\n\n\
-                Event ID: {}\n\
-                Room ID: {}\n\
-                Sent By: {}\n\n\
-                Report Score: {}\n\
-                Report Reason: {}",
+                Event ID: {:?}\n\
+                Room ID: {:?}\n\
+                Sent By: {:?}\n\n\
+                Report Score: {:?}\n\
+                Report Reason: {:?}",
                 sender_user, pdu.event_id, pdu.room_id, pdu.sender, body.score, body.reason
             ),
             format!(
-                "<details><summary>Report received from: <a href=\"https://matrix.to/#/{0}\">{0}\
-                </a></summary><ul><li>Event Info<ul><li>Event ID: <code>{1}</code>\
-                <a href=\"https://matrix.to/#/{2}/{1}\">🔗</a></li><li>Room ID: <code>{2}</code>\
-                </li><li>Sent By: <a href=\"https://matrix.to/#/{3}\">{3}</a></li></ul></li><li>\
-                Report Info<ul><li>Report Score: {4}</li><li>Report Reason: {5}</li></ul></li>\
+                "<details><summary>Report received from: <a href=\"https://matrix.to/#/{0:?}\">{0:?}\
+                </a></summary><ul><li>Event Info<ul><li>Event ID: <code>{1:?}</code>\
+                <a href=\"https://matrix.to/#/{2:?}/{1:?}\">🔗</a></li><li>Room ID: <code>{2:?}</code>\
+                </li><li>Sent By: <a href=\"https://matrix.to/#/{3:?}\">{3:?}</a></li></ul></li><li>\
+                Report Info<ul><li>Report Score: {4:?}</li><li>Report Reason: {5}</li></ul></li>\
                 </ul></details>",
                 sender_user,
                 pdu.event_id,
                 pdu.room_id,
                 pdu.sender,
                 body.score,
-                HtmlEscape(&body.reason)
+                HtmlEscape(&body.reason.clone().unwrap_or(String::new()))
             ),
         ));
 
diff --git a/src/server_server.rs b/src/server_server.rs
index 9dc261708eefad53260750dc6b85185e9440fa98..56f5b9dfb3628749abc9f727a2a3c66d23fa901d 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -30,7 +30,7 @@
             },
             query::{get_profile_information, get_room_information},
             transactions::{
-                edu::{DeviceListUpdateContent, DirectDeviceContent, Edu},
+                edu::{DeviceListUpdateContent, DirectDeviceContent, Edu, SigningKeyUpdateContent},
                 send_transaction_message,
             },
         },
@@ -840,6 +840,22 @@ pub async fn send_transaction_message_route(
                 db.transaction_ids
                     .add_txnid(&sender, None, &message_id, &[])?;
             }
+            Edu::SigningKeyUpdate(SigningKeyUpdateContent {
+                user_id,
+                master_key,
+                self_signing_key,
+            }) => {
+                if let Some(master_key) = master_key {
+                    db.users.add_cross_signing_keys(
+                        &user_id,
+                        &master_key,
+                        &self_signing_key,
+                        &None,
+                        &db.rooms,
+                        &db.globals,
+                    )?;
+                }
+            }
             Edu::_Custom(_) => {}
         }
     }
@@ -2998,6 +3014,12 @@ pub async fn get_devices_route(
                 })
             })
             .collect(),
+        master_key: db
+            .users
+            .get_master_key(&body.user_id, |u| u == &body.user_id)?,
+        self_signing_key: db
+            .users
+            .get_self_signing_key(&body.user_id, |u| u == &body.user_id)?,
     })
 }