diff --git a/include/mtx/events/voip.hpp b/include/mtx/events/voip.hpp
index ee9dbe1453025e39a41ddd187016bd4a5559f7f1..8e1a2a6e7d81e00e15a57cda9e96fe473944ba1e 100644
--- a/include/mtx/events/voip.hpp
+++ b/include/mtx/events/voip.hpp
@@ -22,7 +22,7 @@ struct CallInvite
         //! The SDP text of the session description.
         std::string sdp;
         //! The version of the VoIP specification this message adheres to.
-        uint16_t version;
+        std::string version;
         //! The time in milliseconds that the invite is valid for.
         uint32_t lifetime;
 };
@@ -51,7 +51,7 @@ struct CallCandidates
         //! Array of objects describing the candidates.
         std::vector<Candidate> candidates;
         //! The version of the VoIP specification this message adheres to.
-        uint16_t version;
+        std::string version;
 };
 
 void
@@ -68,7 +68,7 @@ struct CallAnswer
         //! The SDP text of the session description.
         std::string sdp;
         //! The version of the VoIP specification this message adheres to.
-        uint16_t version;
+        std::string version;
 };
 
 void
@@ -90,7 +90,7 @@ struct CallHangUp
         //! The ID of the call this event relates to.
         std::string call_id;
         //! The version of the VoIP specification this message adheres to.
-        uint16_t version;
+        std::string version;
         //! The reason for the call hang up.
         Reason reason = Reason::User;
 };
diff --git a/lib/structs/events/voip.cpp b/lib/structs/events/voip.cpp
index 65f21439af7c8b4b5ad88db4eb293aa6d25ae5e0..0feafd7106a759e486e1ac24ab4f0be7af68adb9 100644
--- a/lib/structs/events/voip.cpp
+++ b/lib/structs/events/voip.cpp
@@ -1,9 +1,20 @@
+#include <string>
+
 #include "mtx/events/voip.hpp"
 
 #include <nlohmann/json.hpp>
 
 using json = nlohmann::json;
 
+namespace {
+std::string
+version(const json &obj)
+{
+        auto v = obj.at("version");
+        return v.is_number() ? "0" : v.get<std::string>();
+}
+}
+
 namespace mtx::events::msg {
 
 // m.call.invite
@@ -12,7 +23,7 @@ from_json(const json &obj, CallInvite &content)
 {
         content.call_id  = obj.at("call_id").get<std::string>();
         content.sdp      = obj.at("offer").at("sdp").get<std::string>();
-        content.version  = obj.at("version").get<uint16_t>();
+        content.version  = version(obj);
         content.lifetime = obj.at("lifetime").get<uint32_t>();
 }
 
@@ -47,7 +58,7 @@ from_json(const json &obj, CallCandidates &content)
 {
         content.call_id    = obj.at("call_id").get<std::string>();
         content.candidates = obj.at("candidates").get<std::vector<CallCandidates::Candidate>>();
-        content.version    = obj.at("version").get<uint16_t>();
+        content.version    = version(obj);
 }
 
 void
@@ -64,7 +75,7 @@ from_json(const json &obj, CallAnswer &content)
 {
         content.call_id = obj.at("call_id").get<std::string>();
         content.sdp     = obj.at("answer").at("sdp").get<std::string>();
-        content.version = obj.at("version").get<uint16_t>();
+        content.version = version(obj);
 }
 
 void
@@ -80,7 +91,7 @@ void
 from_json(const json &obj, CallHangUp &content)
 {
         content.call_id = obj.at("call_id").get<std::string>();
-        content.version = obj.at("version").get<uint16_t>();
+        content.version = version(obj);
         if (obj.count("reason") == 0) {
                 content.reason = CallHangUp::Reason::User;
         } else {