diff --git a/.gitignore b/.gitignore
index b7753d5..e50d552 100644
--- a/.gitignore
+++ b/.gitignore
@@ -92,3 +92,4 @@ Makefile.in
/src/matrix-message-video.c
/src/matrix-message-location.c
/src/matrix-profile.c
+/src/matrix-room.c
diff --git a/src/Makefile.am b/src/Makefile.am
index b00190a..b3ebdd0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,6 +60,7 @@ libmatrix_glib_0_0_la_VALA_SOURCES = \
matrix-message-video.vala \
matrix-message-location.vala \
matrix-profile.vala \
+ matrix-room.vala \
$(NULL)
AM_CPPFLAGS += \
diff --git a/src/matrix-room.vala b/src/matrix-room.vala
new file mode 100644
index 0000000..d52a79d
--- /dev/null
+++ b/src/matrix-room.vala
@@ -0,0 +1,159 @@
+/*
+ * This file is part of matrix-glib-sdk
+ *
+ * matrix-glib-sdk is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * matrix-glib-sdk is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with matrix-glib-sdk. If not, see
+ * .
+ */
+
+/**
+ * Class to hold data about rooms
+ *
+ * The Room class is there to hold different parameters of a room like
+ * its known aliases, its members, etc.
+ */
+public class Matrix.Room : GLib.Object {
+ public string room_id { get; construct; }
+
+ public string[] aliases { get; set; }
+
+ public string? avatar_url { get; set; default = null; }
+
+ public ImageInfo? avatar_info { get; set; default = null; }
+
+ public string? avatar_thumbnail_url { get; set; default = null; }
+
+ public ImageInfo? avatar_thumbnail_info { get; set; default = null; }
+
+ public string? canonical_alias { get; set; default = null; }
+
+ public string? creator { get; set; default = null; }
+
+ public bool federate { get; set; default = false; }
+
+ public GuestAccess guest_access { get; set; default = GuestAccess.UNKNOWN; }
+
+ public HistoryVisibility history_visibility {
+ get; set;
+ default = HistoryVisibility.UNKNOWN;
+ }
+
+ public JoinRules join_rules { get; set; default = JoinRules.UNKNOWN; }
+
+ public string? name { get; set; default = null; }
+
+ public int default_power_level { get; set; default = 0; }
+
+ public int default_event_level { get; set; default = 0; }
+
+ public int default_state_level { get; set; default = 10; }
+
+ public int ban_level { get; set; default = 5; }
+
+ public int kick_level { get; set; default = 5; }
+
+ public int redact_level { get; set; default = 20; }
+
+ public int invite_level { get; set; default = 0; }
+
+ public string? topic { get; set; default = null; }
+
+ public string[] typing_users { get; set; }
+
+ private Gee.HashMap _event_levels =
+ new Gee.HashMap();
+
+ private Gee.HashMap _user_levels =
+ new Gee.HashMap();
+
+ private struct MemberData {
+ Profile profile;
+ bool thirdparty;
+ }
+
+ private Gee.HashMap _members =
+ new Gee.HashMap();
+
+ public
+ Room(string room_id)
+ {
+ Object(room_id : room_id);
+ }
+
+ public void
+ add_member(string user_id, Profile? profile, bool third_party)
+ throws Matrix.Error
+ {
+ MemberData? data = null;
+
+ if ((data = _members[user_id]) != null) {
+ throw new Matrix.Error.ALREADY_EXISTS(
+ "User already exists in that room");
+ }
+
+ data = MemberData();
+
+ if (profile == null) {
+ data.profile = new Profile();
+ } else {
+ data.profile = profile;
+ }
+
+ data.thirdparty = third_party;
+
+ _members[user_id] = data;
+ }
+
+ public Profile
+ get_or_add_member(string user_id, bool third_party = false)
+ throws Matrix.Error
+ {
+ try {
+ return get_member(user_id, null);
+ } catch (Matrix.Error e) {
+ add_member(user_id, null, third_party);
+
+ return get_member(user_id, null);
+ }
+ }
+
+ public Profile
+ get_member(string user_id, out bool third_party)
+ throws Matrix.Error
+ {
+ MemberData? data = null;
+
+ if ((data = _members[user_id]) == null) {
+ throw new Matrix.Error.NOT_FOUND(
+ "No such room member");
+ }
+
+ third_party = data.thirdparty;
+
+ return data.profile;
+ }
+
+ public void
+ remove_member(string user_id)
+ throws Matrix.Error
+ {
+ MemberData? data = null;
+
+ if ((data = _members[user_id]) == null) {
+ throw new Matrix.Error.NOT_FOUND(
+ "No such room member");
+ }
+
+ _members.unset(user_id);
+ }
+}
diff --git a/src/matrix-types.vala b/src/matrix-types.vala
index 83ee3a1..9f56c7a 100644
--- a/src/matrix-types.vala
+++ b/src/matrix-types.vala
@@ -42,6 +42,11 @@ namespace Matrix {
/// yet. Clients getting this message
/// may go online by some means to get
/// the data
+ NOT_FOUND, /// the requested data (e.g. member of
+ /// a room) can not be found
+ ALREADY_EXISTS, /// the data to create (e.g. when
+ /// adding a new member to a Room
+ /// object) already exists
/* Add Matrix-defined error codes under here, prefixing them with
* `MATRIX_ERROR_`, i.e. `M_FORBIDDEN` =>