diff --git a/SharpQuark/ApiResult/ChannelApiResult.cs b/SharpQuark/ApiResult/ChannelApiResult.cs
new file mode 100644
index 0000000000000000000000000000000000000000..73230df8a3fb49778351772b6f7fc49ee6ceec78
--- /dev/null
+++ b/SharpQuark/ApiResult/ChannelApiResult.cs
@@ -0,0 +1,17 @@
+using Newtonsoft.Json;
+using SharpQuark.Objects;
+
+namespace SharpQuark.ApiResult;
+
+
+public class ChannelApiResult : BaseApiResult
+{
+    [JsonProperty("response")]
+    public new required ChannelResponse Response;
+}
+
+public class ChannelResponse : Response
+{
+    [JsonProperty("channel")]
+    public required Channel Channel;
+}
diff --git a/SharpQuark/ApiResult/ChannelMessagesApiResult.cs b/SharpQuark/ApiResult/ChannelMessagesApiResult.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1879435a919c935a32559057809d33d69de28082
--- /dev/null
+++ b/SharpQuark/ApiResult/ChannelMessagesApiResult.cs
@@ -0,0 +1,17 @@
+using Newtonsoft.Json;
+using SharpQuark.Objects;
+
+namespace SharpQuark.ApiResult;
+
+
+public class ChannelMessagesApiResult : BaseApiResult
+{
+    [JsonProperty("response")]
+    public new required ChannelMessagesResponse Response;
+}
+
+public class ChannelMessagesResponse : Response
+{
+    [JsonProperty("messages")]
+    public required Message[] Messages;
+}
diff --git a/SharpQuark/Methods/Auth.cs b/SharpQuark/Methods/Auth.cs
index b6b71272c62f3df9d876ae8620590831c9cf8fdc..3b3bb5578a6784623d6ed9d049ec3cb2f313783e 100644
--- a/SharpQuark/Methods/Auth.cs
+++ b/SharpQuark/Methods/Auth.cs
@@ -22,6 +22,23 @@ public partial class Lightquark
         return parsedApiResult ?? throw new Exception("/auth/token API Result is null");
     }
     
+    // /auth/register
+    public async Task<AuthTokenApiResult> AuthRegister(string email, string password, string username)
+    {
+        var rawApiResult = await Call("/auth/register", "POST", new ApiCallOptions
+        {
+            SkipAuth = true,
+            Body = new
+            {
+                email, password, username
+            }
+        });
+
+        var parsedApiResult = JsonConvert.DeserializeObject<AuthTokenApiResult>(rawApiResult);
+        
+        return parsedApiResult ?? throw new Exception("/auth/register API Result is null");
+    }
+    
     // /auth/refresh
     public async Task<AuthRefreshApiResult> AuthRefresh()
     {
diff --git a/SharpQuark/Methods/Channel.cs b/SharpQuark/Methods/Channel.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b207246cd829ddc808f44243d2e1005ac18f75b0
--- /dev/null
+++ b/SharpQuark/Methods/Channel.cs
@@ -0,0 +1,31 @@
+using Newtonsoft.Json;
+using SharpQuark.ApiResult;
+using SharpQuark.Objects;
+using SharpQuark.Objects.Id;
+
+namespace SharpQuark;
+
+public partial class Lightquark
+{
+    public async Task<ChannelApiResult> ChannelById(ChannelId channelId)
+    {
+        var rawApiResult = await Call($"/channel/{channelId}");
+        
+        var parsedApiResult = JsonConvert.DeserializeObject<ChannelApiResult>(rawApiResult);
+
+        if (parsedApiResult != null) parsedApiResult.Response.Channel.Lq = this;
+
+        return parsedApiResult ?? throw new Exception("/channel/{channelId} API Result is null");
+    }
+
+    public async Task<ChannelMessagesApiResult> ChannelMessages(ChannelId channelId)
+    {
+        
+        var rawApiResult = await Call($"/channel/{channelId}/messages");
+        
+        var parsedApiResult = JsonConvert.DeserializeObject<ChannelMessagesApiResult>(rawApiResult);
+
+        return parsedApiResult ?? throw new Exception("/channel/{channelId}/messages API Result is null");
+    }
+    
+}
\ No newline at end of file
diff --git a/SharpQuark/Methods/User.cs b/SharpQuark/Methods/User.cs
index 3d204d802fde495f839affd908a3872ad96ec708..aecbf88f386facad5d5488cc884e02b815cd55db 100644
--- a/SharpQuark/Methods/User.cs
+++ b/SharpQuark/Methods/User.cs
@@ -1,5 +1,6 @@
 using Newtonsoft.Json;
 using SharpQuark.ApiResult;
+using SharpQuark.Objects.Id;
 
 namespace SharpQuark;
 
@@ -16,7 +17,7 @@ public partial class Lightquark
     }
     
     // /user/:userId
-    public async Task<UserApiResult> UserById(string userId)
+    public async Task<UserApiResult> UserById(UserId userId)
     {
         var rawApiResult = await Call($"/user/{userId}");
 
diff --git a/SharpQuark/Objects/Channel.cs b/SharpQuark/Objects/Channel.cs
new file mode 100644
index 0000000000000000000000000000000000000000..71346a589c408a9d5d92048c4a4656f10e6a42f0
--- /dev/null
+++ b/SharpQuark/Objects/Channel.cs
@@ -0,0 +1,23 @@
+using Newtonsoft.Json;
+using SharpQuark.Objects.Id;
+
+namespace SharpQuark.Objects;
+
+public class Channel
+{
+    [JsonProperty("_id")]
+    public required ChannelId Id;
+    [JsonProperty("name")]
+    public required string Name;
+    [JsonProperty("description")]
+    public string Description = string.Empty;
+    [JsonProperty("quark")]
+    public required QuarkId QuarkId;
+
+    [JsonIgnore] public Lightquark? Lq;
+
+    public async Task<Message[]> GetMessages()
+    {
+        return (await Lq.ChannelMessages(Id)).Response.Messages;
+    }
+};
\ No newline at end of file
diff --git a/SharpQuark/Objects/Id/BaseId.cs b/SharpQuark/Objects/Id/BaseId.cs
new file mode 100644
index 0000000000000000000000000000000000000000..50818858df047f52478e6bc80413c27f28915c26
--- /dev/null
+++ b/SharpQuark/Objects/Id/BaseId.cs
@@ -0,0 +1,11 @@
+namespace SharpQuark.Objects.Id;
+
+public class BaseId(string id)
+{
+    public string Id { get; } = id;
+
+    public override string ToString()
+    {
+        return Id;
+    }
+}
\ No newline at end of file
diff --git a/SharpQuark/Objects/Id/ChannelId.cs b/SharpQuark/Objects/Id/ChannelId.cs
new file mode 100644
index 0000000000000000000000000000000000000000..febbe3dd807e0dd240a2cb9a8f875400597cb415
--- /dev/null
+++ b/SharpQuark/Objects/Id/ChannelId.cs
@@ -0,0 +1,30 @@
+using Newtonsoft.Json;
+
+namespace SharpQuark.Objects.Id;
+
+[JsonConverter(typeof(ChannelIdConverter))]
+public class ChannelId(string id) : BaseId(id);
+
+public class ChannelIdConverter : JsonConverter
+{
+    public override bool CanConvert(Type objectType)
+    {
+        return objectType == typeof(ChannelId);
+    }
+
+    public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
+    {
+        if (reader.TokenType == JsonToken.String)
+        {
+            return new ChannelId((string?)reader.Value ?? string.Empty);
+        }
+        throw new JsonSerializationException("Unexpected token type.");
+    }
+
+    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
+    {
+        value ??= string.Empty;
+        var id = (ChannelId)value;
+        writer.WriteValue(id.Id);
+    }
+}
\ No newline at end of file
diff --git a/SharpQuark/Objects/Id/MessageId.cs b/SharpQuark/Objects/Id/MessageId.cs
new file mode 100644
index 0000000000000000000000000000000000000000..94f4fdc649351eca9ef209046991fc89c2357d11
--- /dev/null
+++ b/SharpQuark/Objects/Id/MessageId.cs
@@ -0,0 +1,32 @@
+using Newtonsoft.Json;
+
+namespace SharpQuark.Objects.Id;
+
+[JsonConverter(typeof(MessageIdConverter))]
+public class MessageId(string id) : BaseId(id);
+
+public class MessageIdConverter : JsonConverter
+{
+    public override bool CanConvert(Type objectType)
+    {
+        return objectType == typeof(MessageId);
+    }
+
+    public override object ReadJson(JsonReader reader, Type objectType, object? existingValue,
+        JsonSerializer serializer)
+    {
+        if (reader.TokenType == JsonToken.String)
+        {
+            return new MessageId((string?)reader.Value ?? string.Empty);
+        }
+
+        throw new JsonSerializationException("Unexpected token type.");
+    }
+
+    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
+    {
+        value ??= string.Empty;
+        var id = (MessageId)value;
+        writer.WriteValue(id.Id);
+    }
+}
\ No newline at end of file
diff --git a/SharpQuark/Objects/Id/QuarkId.cs b/SharpQuark/Objects/Id/QuarkId.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2ecffedfecabb110df3dcbae6d3f156ebfced422
--- /dev/null
+++ b/SharpQuark/Objects/Id/QuarkId.cs
@@ -0,0 +1,30 @@
+using Newtonsoft.Json;
+
+namespace SharpQuark.Objects.Id;
+
+[JsonConverter(typeof(QuarkIdConverter))]
+public class QuarkId(string id) : BaseId(id);
+
+public class QuarkIdConverter : JsonConverter
+{
+    public override bool CanConvert(Type objectType)
+    {
+        return objectType == typeof(QuarkId);
+    }
+
+    public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
+    {
+        if (reader.TokenType == JsonToken.String)
+        {
+            return new QuarkId((string?)reader.Value ?? string.Empty);
+        }
+        throw new JsonSerializationException("Unexpected token type.");
+    }
+
+    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
+    {
+        value ??= string.Empty;
+        var id = (QuarkId)value;
+        writer.WriteValue(id.Id);
+    }
+}
\ No newline at end of file
diff --git a/SharpQuark/Objects/Id/UserId.cs b/SharpQuark/Objects/Id/UserId.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5cbbcd3309dc4f2ce516b897551fc1e20939b2f9
--- /dev/null
+++ b/SharpQuark/Objects/Id/UserId.cs
@@ -0,0 +1,32 @@
+using Newtonsoft.Json;
+
+namespace SharpQuark.Objects.Id;
+
+[JsonConverter(typeof(UserIdConverter))]
+public class UserId(string id) : BaseId(id);
+
+public class UserIdConverter : JsonConverter
+{
+    public override bool CanConvert(Type objectType)
+    {
+        return objectType == typeof(UserId);
+    }
+
+    public override object ReadJson(JsonReader reader, Type objectType, object? existingValue,
+        JsonSerializer serializer)
+    {
+        if (reader.TokenType == JsonToken.String)
+        {
+            return new UserId((string?)reader.Value ?? string.Empty);
+        }
+
+        throw new JsonSerializationException("Unexpected token type.");
+    }
+
+    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
+    {
+        value ??= string.Empty;
+        var id = (UserId)value;
+        writer.WriteValue(id.Id);
+    }
+}
\ No newline at end of file
diff --git a/SharpQuark/Objects/Message.cs b/SharpQuark/Objects/Message.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0d0045ce711d3bb8e6820e026fa51d0b074c4606
--- /dev/null
+++ b/SharpQuark/Objects/Message.cs
@@ -0,0 +1,34 @@
+using Newtonsoft.Json;
+using SharpQuark.Objects.Id;
+
+namespace SharpQuark.Objects;
+
+public class Message
+{
+    [JsonProperty("_id")] 
+    public required MessageId Id;
+    
+    [JsonProperty("authorId")] 
+    public required UserId AuthorId;
+    
+    [JsonProperty("content")]
+    public string Content = string.Empty;
+    
+    [JsonProperty("ua")]
+    public string Agent = "Unknown";
+
+    [JsonProperty("timestamp")] 
+    public required long JsTimestamp;
+
+    [JsonProperty("edited")] 
+    public required bool Edited;
+
+    [JsonProperty("attachments")]
+    public required string[] AttachmentLinks; // TODO: Attachment objects with metadata
+
+    [JsonProperty("specialAttributes")]
+    public object[] SpecialAttributes = [];
+
+    [JsonProperty("author")]
+    public required User Author;
+}
\ No newline at end of file
diff --git a/SharpQuark/Objects/User.cs b/SharpQuark/Objects/User.cs
index b5786722baf1857ddf4ca6562db329d9880ff4de..b19371d659fcde20007fbb304977b7878686cba5 100644
--- a/SharpQuark/Objects/User.cs
+++ b/SharpQuark/Objects/User.cs
@@ -1,6 +1,7 @@
 using System.Runtime.Serialization;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Converters;
+using SharpQuark.Objects.Id;
 
 namespace SharpQuark.Objects;
 
@@ -9,7 +10,7 @@ public class User
     [JsonProperty("isBot")]
     public bool IsBot;
     [JsonProperty("_id")]
-    public required string Id;
+    public required UserId Id;
     [JsonProperty("username")]
     public required string Username;
     [JsonProperty("email")]
diff --git a/SharpQuark/Token/TokenCredential.cs b/SharpQuark/Token/TokenCredential.cs
index 04c4a6c6ee87f697f9e61a5e65a71ca54aa04daf..b8a707548ab7f427eef57f252184c0e1d6c497e7 100644
--- a/SharpQuark/Token/TokenCredential.cs
+++ b/SharpQuark/Token/TokenCredential.cs
@@ -30,4 +30,14 @@ public class TokenCredential(AccessToken accessToken, RefreshToken refreshToken)
         var refreshToken = (RefreshToken)Token.From(res.Response.RefreshToken);
         return new TokenCredential(accessToken, refreshToken);
     }
+    
+    public static async Task<TokenCredential> Register(string email, string password, string username, NetworkInformation networkInformation)
+    {
+        // Create temporary Lightquark instance
+        var tempLq = new Lightquark(new TokenCredential(new AccessToken(), new RefreshToken()), networkInformation, null, "v3", true);
+        var res = await tempLq.AuthRegister(email, password, username);
+        var accessToken = (AccessToken)Token.From(res.Response.AccessToken);
+        var refreshToken = (RefreshToken)Token.From(res.Response.RefreshToken);
+        return new TokenCredential(accessToken, refreshToken);
+    }
 }
\ No newline at end of file
diff --git a/readme.md b/readme.md
index a860e14fa98c9b8b060a25f19a27cce16aae7fb6..33aecba775595c4cd1a39c5e0c6886e6b4aa8416 100644
--- a/readme.md
+++ b/readme.md
@@ -18,6 +18,8 @@ var netInfo = await NetworkInformation.GetNetwork("https://equinox.lightquark.ne
 var tokens = await TokenCredential.Login("email", "password", netInfo);
 // Or alternatively stored tokens can be used to get the TokenCredential directly, for example from strings:
 var tokens2 = new TokenCredential((AccessToken)Token.From("access token"), (RefreshToken)Token.From("refresh token"));
+// Another alternative is to register a new account, since that also returns tokens
+var tokens3 = await TokenCredential.Register("email", "password", "username", netInfo);
 
 // With TokenCredential and NetworkInformation we can actually create the Lightquark instance
 var lq = new Lightquark(tokens, netInfo);