diff --git a/.ci/synapse/Dockerfile b/.ci/synapse/Dockerfile
index fdfc357ba25a56911a4e3a78a6a673ab2fe38c3e..0159caeb198b17c59dd5d686535839b41d3daa9c 100644
--- a/.ci/synapse/Dockerfile
+++ b/.ci/synapse/Dockerfile
@@ -1,4 +1,4 @@
-FROM matrixdotorg/synapse:v1.44.0
+FROM matrixdotorg/synapse:v1.55.0
 
 COPY setup-synapse.sh /setup-synapse.sh
 COPY entrypoint.sh /entrypoint.sh
diff --git a/Makefile b/Makefile
index 7f7ff7b2796605872512ee8212fc611203e532b7..5bdd3d82aa5099694c617527bfbceed264670044 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 FILES=`find lib include tests examples -type f -type f \( -iname "*.cpp" -o -iname "*.hpp" \)`
 
-SYNAPSE_IMAGE="matrixdotorg/synapse:v1.44.0"
+SYNAPSE_IMAGE="matrixdotorg/synapse:v1.55.0"
 
 DEPS_BUILD_DIR=.deps
 DEPS_SOURCE_DIR=deps
diff --git a/include/mtxclient/http/client.hpp b/include/mtxclient/http/client.hpp
index b2d2f605d6b3ada2f13fef27181b2c446183f3f4..7dbd280f6af1b5b51cd2bd0431aac3f2486c6aa3 100644
--- a/include/mtxclient/http/client.hpp
+++ b/include/mtxclient/http/client.hpp
@@ -393,9 +393,12 @@ public:
     //! room.
     void join_room(const std::string &room,
                    const std::vector<std::string> &via,
-                   Callback<mtx::responses::RoomId> cb);
+                   Callback<mtx::responses::RoomId> cb,
+                   const std::string &reason = "");
     //! Leave a room by its room_id.
-    void leave_room(const std::string &room_id, Callback<mtx::responses::Empty> cb);
+    void leave_room(const std::string &room_id,
+                    Callback<mtx::responses::Empty> cb,
+                    const std::string &reason = "");
     //! Knock on a room.
     void knock_room(const std::string &room_id,
                     const std::vector<std::string> &via,
diff --git a/lib/http/client.cpp b/lib/http/client.cpp
index be837028a8f5b075fb625dc7e887433b76edcc80..18af2e57e23db72487ef29c6dc4a3d4d2b089054 100644
--- a/lib/http/client.cpp
+++ b/lib/http/client.cpp
@@ -531,7 +531,8 @@ Client::join_room(const std::string &room, Callback<mtx::responses::RoomId> call
 void
 Client::join_room(const std::string &room,
                   const std::vector<std::string> &via,
-                  Callback<mtx::responses::RoomId> callback)
+                  Callback<mtx::responses::RoomId> callback,
+                  const std::string &reason)
 {
     using mtx::client::utils::url_encode;
     std::string query;
@@ -541,9 +542,13 @@ Client::join_room(const std::string &room,
             query += "&server_name=" + url_encode(via[i]);
         }
     }
-    auto api_path = "/client/r0/join/" + url_encode(room) + query;
+    auto api_path = "/client/v3/join/" + url_encode(room) + query;
+
+    auto body = nlohmann::json::object();
+    if (!reason.empty())
+        body["reason"] = reason;
 
-    post<std::string, mtx::responses::RoomId>(api_path, "{}", std::move(callback));
+    post<std::string, mtx::responses::RoomId>(api_path, body.dump(), std::move(callback));
 }
 
 void
@@ -560,7 +565,7 @@ Client::knock_room(const std::string &room,
             query += "&server_name=" + url_encode(via[i]);
         }
     }
-    auto api_path = "/client/r0/knock/" + url_encode(room) + query;
+    auto api_path = "/client/v3/knock/" + url_encode(room) + query;
 
     auto body = nlohmann::json::object();
     if (!reason.empty())
@@ -570,11 +575,17 @@ Client::knock_room(const std::string &room,
 }
 
 void
-Client::leave_room(const std::string &room_id, Callback<mtx::responses::Empty> callback)
+Client::leave_room(const std::string &room_id,
+                   Callback<mtx::responses::Empty> callback,
+                   const std::string &reason)
 {
-    auto api_path = "/client/r0/rooms/" + mtx::client::utils::url_encode(room_id) + "/leave";
+    auto api_path = "/client/v3/rooms/" + mtx::client::utils::url_encode(room_id) + "/leave";
+
+    auto body = nlohmann::json::object();
+    if (!reason.empty())
+        body["reason"] = reason;
 
-    post<std::string, mtx::responses::Empty>(api_path, "{}", std::move(callback));
+    post<std::string, mtx::responses::Empty>(api_path, body.dump(), std::move(callback));
 }
 
 void
diff --git a/tests/client_api.cpp b/tests/client_api.cpp
index 7f044cdcaf01faeb92f16c6401fb176aa840b98e..1678386c0937aac05ac31dc8276765d62b159b64 100644
--- a/tests/client_api.cpp
+++ b/tests/client_api.cpp
@@ -968,7 +968,7 @@ TEST(ClientAPI, Versions)
     mtx_client->versions([](const mtx::responses::Versions &res, RequestErr err) {
         check_error(err);
 
-        EXPECT_EQ(res.versions.size(), 7);
+        EXPECT_EQ(res.versions.size(), 10);
         EXPECT_EQ(res.versions.at(0), "r0.0.1");
         EXPECT_EQ(res.versions.at(1), "r0.1.0");
         EXPECT_EQ(res.versions.at(2), "r0.2.0");
@@ -976,6 +976,9 @@ TEST(ClientAPI, Versions)
         EXPECT_EQ(res.versions.at(4), "r0.4.0");
         EXPECT_EQ(res.versions.at(5), "r0.5.0");
         EXPECT_EQ(res.versions.at(6), "r0.6.0");
+        EXPECT_EQ(res.versions.at(7), "r0.6.1");
+        EXPECT_EQ(res.versions.at(8), "v1.1");
+        EXPECT_EQ(res.versions.at(9), "v1.2");
     });
 
     mtx_client->close();