diff --git a/go.mod b/go.mod
index 3ed58b94ec254741b1f00ce316c74cafe4edf712..3ddeac92e64e2f19168da50c04e886aa3def5ad2 100644
--- a/go.mod
+++ b/go.mod
@@ -7,11 +7,11 @@ require (
 	github.com/hack-pad/go-indexeddb v0.2.0
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/jwalterweatherman v1.1.0
-	gitlab.com/elixxir/client/v4 v4.3.9-0.20221216005432-2c3c82c2e7e8
-	gitlab.com/elixxir/crypto v0.0.7-0.20221214192244-6783272c04a0
+	gitlab.com/elixxir/client/v4 v4.3.9-0.20221220181602-504f5e86d446
+	gitlab.com/elixxir/crypto v0.0.7-0.20221220181038-c77d1bf49838
 	gitlab.com/elixxir/primitives v0.0.3-0.20221214192222-988b44a6958a
 	gitlab.com/xx_network/crypto v0.0.5-0.20221121220724-8eefdbb0eb46
-	gitlab.com/xx_network/primitives v0.0.4-0.20221209210320-376735467d58
+	gitlab.com/xx_network/primitives v0.0.4-0.20221219230308-4b5550a9247d
 	golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
 )
 
diff --git a/go.sum b/go.sum
index 6b6298a6e82ad40d6e1dba4f05a9977c9e24da2f..c8222ebed82a201e3a0bcf877e0004b5bedc0cf7 100644
--- a/go.sum
+++ b/go.sum
@@ -369,12 +369,12 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
 github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f h1:yXGvNBqzZwAhDYlSnxPRbgor6JWoOt1Z7s3z1O9JR40=
 gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
-gitlab.com/elixxir/client/v4 v4.3.9-0.20221216005432-2c3c82c2e7e8 h1:MFGMCpHVR7u2vmYcyxB0kmsiElUTXKzB7JlqeCwQE0c=
-gitlab.com/elixxir/client/v4 v4.3.9-0.20221216005432-2c3c82c2e7e8/go.mod h1:gxRW2YXDxCzESBnYqMDKH2I25TGv226TDujdQno3JQw=
+gitlab.com/elixxir/client/v4 v4.3.9-0.20221220181602-504f5e86d446 h1:18Y4M0/KWLNshps2gyQ97+uCbSul3RezOGtdVM4e07E=
+gitlab.com/elixxir/client/v4 v4.3.9-0.20221220181602-504f5e86d446/go.mod h1:rZcfB8jkloBkUqKsdUAifWaGmSmJyZUOmDKnqAwmias=
 gitlab.com/elixxir/comms v0.0.4-0.20221215214627-7807bfdde33a h1:DuqDqWc5cWjZ3qk98K1Bf9y1dYlyCeIigFmkHWDKc1Q=
 gitlab.com/elixxir/comms v0.0.4-0.20221215214627-7807bfdde33a/go.mod h1:B2Yek4mCbtN2aXZkyZcUffd3sTEZ5WgKD0mRBSVYtF8=
-gitlab.com/elixxir/crypto v0.0.7-0.20221214192244-6783272c04a0 h1:dwCf7wKv2DCuYZZ394bSQWdUOXiABLsEyDvXZUOo83o=
-gitlab.com/elixxir/crypto v0.0.7-0.20221214192244-6783272c04a0/go.mod h1:oRh3AwveOEvpk9E3kRcMGK8fImcEnN0PY4jr9HDgQE8=
+gitlab.com/elixxir/crypto v0.0.7-0.20221220181038-c77d1bf49838 h1:s93tL+O9LaHow14edbJF0/tAKGvKkoUJCeHPNtkB9iY=
+gitlab.com/elixxir/crypto v0.0.7-0.20221220181038-c77d1bf49838/go.mod h1:oRh3AwveOEvpk9E3kRcMGK8fImcEnN0PY4jr9HDgQE8=
 gitlab.com/elixxir/ekv v0.2.1 h1:dtwbt6KmAXG2Tik5d60iDz2fLhoFBgWwST03p7T+9Is=
 gitlab.com/elixxir/ekv v0.2.1/go.mod h1:USLD7xeDnuZEavygdrgzNEwZXeLQJK/w1a+htpN+JEU=
 gitlab.com/elixxir/primitives v0.0.3-0.20221214192222-988b44a6958a h1:F17FfEjS+/uDI/TTYQD21S5JvNZ9+p9bieau2nyLCzo=
@@ -383,8 +383,8 @@ gitlab.com/xx_network/comms v0.0.4-0.20221215214252-1275cef8760e h1:l+FiCBP2Lc1+
 gitlab.com/xx_network/comms v0.0.4-0.20221215214252-1275cef8760e/go.mod h1:FR/OyruSuob6+xzSZtk+rXlncbRr6nDKFypX3vwtkFc=
 gitlab.com/xx_network/crypto v0.0.5-0.20221121220724-8eefdbb0eb46 h1:6AHgUpWdJ72RVTTdJSvfThZiYTQNUnrPaTCl/EkRLpg=
 gitlab.com/xx_network/crypto v0.0.5-0.20221121220724-8eefdbb0eb46/go.mod h1:acWUBKCpae/XVaQF7J9RnLAlBT13i5r7gnON+mrIxBk=
-gitlab.com/xx_network/primitives v0.0.4-0.20221209210320-376735467d58 h1:HpeUIf1gIIelLH3LHxEf3/GalecbbtZnOnIegJHALoc=
-gitlab.com/xx_network/primitives v0.0.4-0.20221209210320-376735467d58/go.mod h1:wUxbEBGOBJZ/RkAiVAltlC1uIlIrU0dE113Nq7HiOhw=
+gitlab.com/xx_network/primitives v0.0.4-0.20221219230308-4b5550a9247d h1:D9hEtiQ7xj0yFBkDkb4X4S95RfNoeXxtB1eE4UuFHtk=
+gitlab.com/xx_network/primitives v0.0.4-0.20221219230308-4b5550a9247d/go.mod h1:wUxbEBGOBJZ/RkAiVAltlC1uIlIrU0dE113Nq7HiOhw=
 gitlab.com/xx_network/ring v0.0.3-0.20220902183151-a7d3b15bc981 h1:1s0vX9BbkiD0IVXwr3LOaTBcq1wBrWcUWMBK0s8r0Z0=
 gitlab.com/xx_network/ring v0.0.3-0.20220902183151-a7d3b15bc981/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
diff --git a/indexedDb/channels/implementation.go b/indexedDb/channels/implementation.go
index 8d68e76b561509e29676bcd5d114f7517d3b4822..9fcdf5fdccf867ccedb9eb6f0123db433d83e7c9 100644
--- a/indexedDb/channels/implementation.go
+++ b/indexedDb/channels/implementation.go
@@ -40,6 +40,13 @@ type wasmModel struct {
 	updateMux         sync.Mutex
 }
 
+// DeleteMessage removes a message with the given messageID from storage.
+func (w *wasmModel) DeleteMessage(messageID cryptoChannel.MessageID) error {
+	msgId := js.ValueOf(base64.StdEncoding.EncodeToString(messageID.Bytes()))
+	return indexedDb.DeleteIndex(w.db, messageStoreName,
+		messageStoreMessageIndex, pkeyName, msgId)
+}
+
 // JoinChannel is called whenever a channel is joined locally.
 func (w *wasmModel) JoinChannel(channel *cryptoBroadcast.Channel) {
 	parentErr := errors.New("failed to JoinChannel")
@@ -76,35 +83,12 @@ func (w *wasmModel) JoinChannel(channel *cryptoBroadcast.Channel) {
 func (w *wasmModel) LeaveChannel(channelID *id.ID) {
 	parentErr := errors.New("failed to LeaveChannel")
 
-	// Prepare the Transaction
-	txn, err := w.db.Transaction(idb.TransactionReadWrite, channelsStoreName)
-	if err != nil {
-		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
-			"Unable to create Transaction: %+v", err))
-		return
-	}
-	store, err := txn.ObjectStore(channelsStoreName)
-	if err != nil {
-		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
-			"Unable to get ObjectStore: %+v", err))
-		return
-	}
-
-	// Perform the operation
-	_, err = store.Delete(js.ValueOf(channelID.String()))
-	if err != nil {
-		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
-			"Unable to Delete Channel: %+v", err))
-		return
-	}
-
-	// Wait for the operation to return
-	ctx, cancel := indexedDb.NewContext()
-	err = txn.Await(ctx)
-	cancel()
+	// Delete the channel from storage
+	err := indexedDb.Delete(w.db, channelsStoreName,
+		js.ValueOf(channelID.String()))
 	if err != nil {
 		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
-			"Deleting Channel failed: %+v", err))
+			"Unable to delete Channel: %+v", err))
 		return
 	}
 
diff --git a/indexedDb/channels/implementation_test.go b/indexedDb/channels/implementation_test.go
index 19f5751eff986c207610154df1cf485f6c82c4b2..0a7ccc77827ee9c32c3e218d67ab0a746ef59e1e 100644
--- a/indexedDb/channels/implementation_test.go
+++ b/indexedDb/channels/implementation_test.go
@@ -63,8 +63,52 @@ func TestWasmModel_msgIDLookup(t *testing.T) {
 	}
 }
 
-// Test wasmModel.UpdateSentStatus happy path and ensure fields don't change.
-func Test_wasmModel_UpdateSentStatus(t *testing.T) {
+// Happy path, insert message and delete it
+func TestWasmModel_DeleteMessage(t *testing.T) {
+	storage.GetLocalStorage().Clear()
+	testString := "test"
+	testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1})
+	eventModel, err := newWASMModel(testString, nil, dummyCallback)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+
+	// Insert a message
+	testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
+		testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(),
+		time.Second, 0, 0, false, false, channels.Sent)
+	_, err = eventModel.receiveHelper(testMsg, false)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+
+	// Check the resulting status
+	results, err := indexedDb.Dump(eventModel.db, messageStoreName)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+	if len(results) != 1 {
+		t.Fatalf("Expected 1 message to exist")
+	}
+
+	// Delete the message
+	err = eventModel.DeleteMessage(testMsgId)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+
+	// Check the resulting status
+	results, err = indexedDb.Dump(eventModel.db, messageStoreName)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+	if len(results) != 0 {
+		t.Fatalf("Expected no messages to exist")
+	}
+}
+
+// Test wasmModel.UpdateFromUUID happy path and ensure fields don't change.
+func Test_wasmModel_UpdateFromUUID(t *testing.T) {
 	storage.GetLocalStorage().Clear()
 	testString := "test"
 	testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1})
diff --git a/indexedDb/utils.go b/indexedDb/utils.go
index 1aa6ff45805c1c65b9ad27e51a8b0590e7b8201d..df25de1c7dc4d005d2d7f8da0e034de31df652e5 100644
--- a/indexedDb/utils.go
+++ b/indexedDb/utils.go
@@ -32,6 +32,7 @@ func NewContext() (context.Context, context.CancelFunc) {
 }
 
 // Get is a generic helper for getting values from the given [idb.ObjectStore].
+// Only usable by primary key.
 func Get(db *idb.Database, objectStoreName string, key js.Value) (js.Value, error) {
 	parentErr := errors.Errorf("failed to Get %s/%s", objectStoreName, key)
 
@@ -74,7 +75,7 @@ func Get(db *idb.Database, objectStoreName string, key js.Value) (js.Value, erro
 
 // GetIndex is a generic helper for getting values from the given
 // [idb.ObjectStore] using the given [idb.Index].
-func GetIndex(db *idb.Database, objectStoreName string,
+func GetIndex(db *idb.Database, objectStoreName,
 	indexName string, key js.Value) (js.Value, error) {
 	parentErr := errors.Errorf("failed to GetIndex %s/%s/%s",
 		objectStoreName, indexName, key)
@@ -147,17 +148,18 @@ func Put(db *idb.Database, objectStoreName string, value js.Value) (*idb.Request
 	if err != nil {
 		return nil, errors.Errorf("Putting value failed: %+v", err)
 	}
-	jww.DEBUG.Printf("Successfully put value in %s: %v",
+	jww.DEBUG.Printf("Successfully put value in %s: %s",
 		objectStoreName, utils.JsToJson(value))
 	return request, nil
 }
 
 // Delete is a generic helper for removing values from the given [idb.ObjectStore].
+// Only usable by primary key.
 func Delete(db *idb.Database, objectStoreName string, key js.Value) error {
 	parentErr := errors.Errorf("failed to Delete %s/%s", objectStoreName, key)
 
 	// Prepare the Transaction
-	txn, err := db.Transaction(idb.TransactionReadOnly, objectStoreName)
+	txn, err := db.Transaction(idb.TransactionReadWrite, objectStoreName)
 	if err != nil {
 		return errors.WithMessagef(parentErr,
 			"Unable to create Transaction: %+v", err)
@@ -169,20 +171,44 @@ func Delete(db *idb.Database, objectStoreName string, key js.Value) error {
 	}
 
 	// Perform the operation
-	deleteRequest, err := store.Delete(key)
+	_, err = store.Delete(key)
 	if err != nil {
 		return errors.WithMessagef(parentErr,
-			"Unable to Get from ObjectStore: %+v", err)
+			"Unable to Delete from ObjectStore: %+v", err)
 	}
 
 	// Wait for the operation to return
 	ctx, cancel := NewContext()
-	err = deleteRequest.Await(ctx)
+	err = txn.Await(ctx)
 	cancel()
 	if err != nil {
 		return errors.WithMessagef(parentErr,
-			"Unable to delete from ObjectStore: %+v", err)
+			"Unable to Delete from ObjectStore: %+v", err)
+	}
+	jww.DEBUG.Printf("Successfully deleted value at %s/%s",
+		objectStoreName, utils.JsToJson(key))
+	return nil
+}
+
+// DeleteIndex is a generic helper for removing values from the
+// given [idb.ObjectStore] using the given [idb.Index]. Requires passing
+// in the name of the primary key for the store.
+func DeleteIndex(db *idb.Database, objectStoreName,
+	indexName, pkeyName string, key js.Value) error {
+	parentErr := errors.Errorf("failed to DeleteIndex %s/%s", objectStoreName, key)
+
+	value, err := GetIndex(db, objectStoreName, indexName, key)
+	if err != nil {
+		return errors.WithMessagef(parentErr, "%+v", err)
 	}
+
+	err = Delete(db, objectStoreName, value.Get(pkeyName))
+	if err != nil {
+		return errors.WithMessagef(parentErr, "%+v", err)
+	}
+
+	jww.DEBUG.Printf("Successfully deleted value at %s/%s/%s",
+		objectStoreName, indexName, utils.JsToJson(key))
 	return nil
 }
 
diff --git a/main.go b/main.go
index 2a08720056a3c07df4d0e17174601e1f2bb34376..7194cffffd42c48edb25f495b372fcc009efe67f 100644
--- a/main.go
+++ b/main.go
@@ -82,6 +82,7 @@ func main() {
 	js.Global().Set("GetChannelJSON", js.FuncOf(wasm.GetChannelJSON))
 	js.Global().Set("GetChannelInfo", js.FuncOf(wasm.GetChannelInfo))
 	js.Global().Set("GetShareUrlType", js.FuncOf(wasm.GetShareUrlType))
+	js.Global().Set("ValidForever", js.FuncOf(wasm.ValidForever))
 	js.Global().Set("IsNicknameValid", js.FuncOf(wasm.IsNicknameValid))
 	js.Global().Set("NewChannelsDatabaseCipher",
 		js.FuncOf(wasm.NewChannelsDatabaseCipher))
diff --git a/wasm/channels.go b/wasm/channels.go
index 429ec45e79c5ce6bb202f087541d2052f107f17b..0c1be8ca72a4dd8f4da7657cc4c2ee72fa405259 100644
--- a/wasm/channels.go
+++ b/wasm/channels.go
@@ -12,6 +12,7 @@ package wasm
 import (
 	"encoding/base64"
 	"encoding/json"
+	"errors"
 	"gitlab.com/elixxir/client/v4/channels"
 	channelsDb "gitlab.com/elixxir/xxdk-wasm/indexedDb/channels"
 	"gitlab.com/xx_network/primitives/id"
@@ -88,11 +89,14 @@ func (cm *ChannelsManager) GetID(js.Value, []js.Value) any {
 }
 
 // GenerateChannelIdentity creates a new private channel identity
-// ([channel.PrivateIdentity]). The public component can be retrieved as JSON
-// via [GetPublicChannelIdentityFromPrivate].
+// ([channel.PrivateIdentity]) from scratch and assigns it a codename.
+//
+// The public component can be retrieved as JSON via
+// [GetPublicChannelIdentityFromPrivate].
 //
 // Parameters:
-//   - args[0] - ID of [Cmix] object in tracker (int).
+//   - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
+//     using [Cmix.GetID].
 //
 // Returns:
 //   - Marshalled bytes of [channel.PrivateIdentity] (Uint8Array).
@@ -110,8 +114,8 @@ func GenerateChannelIdentity(_ js.Value, args []js.Value) any {
 // identityMap stores identities previously generated by ConstructIdentity.
 var identityMap sync.Map
 
-// ConstructIdentity constructs a [channel.Identity] from a user's public key
-// and codeset version.
+// ConstructIdentity creates a codename in a public [channel.Identity] from an
+// extant identity for a given codeset version.
 //
 // Parameters:
 //   - args[0] - The Ed25519 public key (Uint8Array).
@@ -170,7 +174,8 @@ func constructIdentity(_ js.Value, args []js.Value) any {
 //
 // Parameters:
 //   - args[0] - The password used to encrypt the identity (string).
-//   - args[2] - The encrypted data (Uint8Array).
+//   - args[2] - The encrypted data from [ChannelsManager.ExportPrivateIdentity]
+//     (Uint8Array).
 //
 // Returns:
 //   - JSON of [channel.PrivateIdentity] (Uint8Array).
@@ -215,7 +220,7 @@ func GetPublicChannelIdentity(_ js.Value, args []js.Value) any {
 //
 // Parameters:
 //   - args[0] - Bytes of the private identity
-//     (channel.PrivateIdentity]) (Uint8Array).
+//     ([channel.PrivateIdentity]) (Uint8Array).
 //
 // Returns:
 //   - JSON of the public identity ([channel.Identity]) (Uint8Array).
@@ -267,7 +272,8 @@ func NewChannelsManager(_ js.Value, args []js.Value) any {
 	return newChannelsManagerJS(cm)
 }
 
-// LoadChannelsManager loads an existing [ChannelsManager].
+// LoadChannelsManager loads an existing [ChannelsManager] for the given storage
+// tag.
 //
 // This is for loading a manager for an identity that has already been created.
 // The channel manager should have previously been created with
@@ -601,8 +607,9 @@ func GetChannelInfo(_ js.Value, args []js.Value) any {
 	return utils.CopyBytesToJS(ci)
 }
 
-// GenerateChannel creates a new channel with the user as the admin. This
-// function only create a channel and does not join it.
+// GenerateChannel creates a new channel with the user as the admin and returns
+// the pretty print of the channel. This function only create a channel and does
+// not join it.
 //
 // The private key is saved to storage and can be accessed with
 // [ChannelsManager.ExportChannelAdminKey].
@@ -643,12 +650,12 @@ func (cm *ChannelsManager) GenerateChannel(_ js.Value, args []js.Value) any {
 	return prettyPrint
 }
 
-// JoinChannel joins the given channel. It will fail if the channel has already
-// been joined.
+// JoinChannel joins the given channel. It will return the error
+// [channels.ChannelAlreadyExistsErr] if the channel has already been joined.
 //
 // Parameters:
 //   - args[0] - A portable channel string. Should be received from another user
-//     or generated via [GenerateChannel] (string).
+//     or generated via [ChannelsManager.GenerateChannel] (string).
 //
 // The pretty print will be of the format:
 //
@@ -668,8 +675,8 @@ func (cm *ChannelsManager) JoinChannel(_ js.Value, args []js.Value) any {
 	return utils.CopyBytesToJS(ci)
 }
 
-// LeaveChannel leaves the given channel. It will return an error if the channel
-// was not previously joined.
+// LeaveChannel leaves the given channel. It will return the error
+// [channels.ChannelDoesNotExistsErr] if the channel was not previously joined.
 //
 // Parameters:
 //   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
@@ -691,8 +698,11 @@ func (cm *ChannelsManager) LeaveChannel(_ js.Value, args []js.Value) any {
 // ReplayChannel replays all messages from the channel within the network's
 // memory (~3 weeks) over the event model.
 //
+// Returns the error [channels.ChannelDoesNotExistsErr] if the channel was not
+// previously joined.
+//
 // Parameters:
-//   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - args[0] - Marshalled bytes of the channel's [id.ID] (Uint8Array).
 //
 // Returns:
 //   - Throws a TypeError if the replay fails.
@@ -754,8 +764,8 @@ type ShareURL struct {
 // channel. If it is set to 0, then it can be shared unlimited times. The max
 // uses is set as a URL parameter using the key [broadcast.MaxUsesKey]. Note
 // that this number is also encoded in the secret data for private and secret
-// URLs, so if the number is changed in the URL, is will be verified when
-// calling [DecodePublicURL] or [DecodePrivateURL]. There is no enforcement for
+// URLs, so if the number is changed in the URL, it will be verified when
+// calling [DecodePublicURL] and [DecodePrivateURL]. There is no enforcement for
 // public URLs.
 //
 // Parameters:
@@ -791,7 +801,7 @@ func (cm *ChannelsManager) GetShareURL(_ js.Value, args []js.Value) any {
 //
 // Returns:
 //   - An int that corresponds to the [broadcast.PrivacyLevel] as outlined
-//     below.
+//     below (int).
 //   - Throws a TypeError if parsing the URL fails.
 //
 // Possible returns:
@@ -813,24 +823,33 @@ func GetShareUrlType(_ js.Value, args []js.Value) any {
 // Channel Sending Methods and Reports                                        //
 ////////////////////////////////////////////////////////////////////////////////
 
+// ValidForever returns the value to use for validUntil when you want a message
+// to be available for the maximum amount of time.
+//
+// Returns:
+//   - The maximum amount of time (int).
+func ValidForever(js.Value, []js.Value) any {
+	return bindings.ValidForever()
+}
+
 // SendGeneric is used to send a raw message over a channel. In general, it
-// should be wrapped in a function which defines the wire protocol. If the final
-// message, before being sent over the wire, is too long, this will return an
-// error. Due to the underlying encoding using compression, it isn't possible to
-// define the largest payload that can be sent, but it will always be possible
-// to send a payload of 802 bytes at minimum. The meaning of validUntil depends
-// on the use case.
+// should be wrapped in a function that defines the wire protocol.
+//
+// If the final message, before being sent over the wire, is too long, this
+// will return an error. Due to the underlying encoding using compression,
+// it is not possible to define the largest payload that can be sent, but it
+// will always be possible to send a payload of 802 bytes at minimum.
 //
 // Parameters:
 //   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
 //   - args[1] - The message type of the message. This will be a valid
 //     [channels.MessageType] (int).
 //   - args[2] - The contents of the message (Uint8Array).
-//   - args[3] - The lease of the message. This will be how long the message is
-//     valid until, in milliseconds. As per the [channels.Manager]
-//     documentation, this has different meanings depending on the use case.
-//     These use cases may be generic enough that they will not be enumerated
-//     here (int).
+//   - args[3] - The lease of the message. This will be how long the
+//     message is available from the network, in milliseconds (int). As per the
+//     [channels.Manager] documentation, this has different meanings depending
+//     on the use case. These use cases may be generic enough that they will not
+//     be enumerated here. Use [ValidForever] to last the max message life.
 //   - args[4] - Set tracked to true if the message should be tracked in the
 //     sendTracker, which allows messages to be shown locally before they are
 //     received on the network. In general, all messages that will be displayed
@@ -863,6 +882,7 @@ func (cm *ChannelsManager) SendGeneric(_ js.Value, args []js.Value) any {
 }
 
 // SendMessage is used to send a formatted message over a channel.
+//
 // Due to the underlying encoding using compression, it isn't possible to define
 // the largest payload that can be sent, but it will always be possible to send
 // a payload of 798 bytes at minimum.
@@ -873,11 +893,11 @@ func (cm *ChannelsManager) SendGeneric(_ js.Value, args []js.Value) any {
 // Parameters:
 //   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
 //   - args[1] - The contents of the message (string).
-//   - args[2] - The lease of the message. This will be how long the message is
-//     valid until, in milliseconds. As per the [channels.Manager]
-//     documentation, this has different meanings depending on the use case.
-//     These use cases may be generic enough that they will not be enumerated
-//     here (int).
+//   - args[2] - The lease of the message. This will be how long the
+//     message is available from the network, in milliseconds (int). As per the
+//     [channels.Manager] documentation, this has different meanings depending
+//     on the use case. These use cases may be generic enough that they will not
+//     be enumerated here. Use [ValidForever] to last the max message life.
 //   - args[3] - JSON of [xxdk.CMIXParams]. If left empty
 //     [bindings.GetDefaultCMixParams] will be used internally (Uint8Array).
 //
@@ -903,15 +923,14 @@ func (cm *ChannelsManager) SendMessage(_ js.Value, args []js.Value) any {
 	return utils.CreatePromise(promiseFn)
 }
 
-// SendReply is used to send a formatted message over a channel. Due to the
-// underlying encoding using compression, it isn't possible to define the
-// largest payload that can be sent, but it will always be possible to send a
-// payload of 766 bytes at minimum.
+// SendReply is used to send a formatted message over a channel.
+//
+// Due to the underlying encoding using compression, it is not possible to
+// define the largest payload that can be sent, but it will always be possible
+// to send a payload of 766 bytes at minimum.
 //
-// If the message ID the reply is sent to is nonexistent, the other side will
-// post the message as a normal message and not a reply. The message will auto
-// delete validUntil after the round it is sent in, lasting forever if
-// [channels.ValidForever] is used.
+// If the message ID that the reply is sent to does not exist, then the other
+// side will post the message as a normal message and not as a reply.
 //
 // Parameters:
 //   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
@@ -923,11 +942,11 @@ func (cm *ChannelsManager) SendMessage(_ js.Value, args []js.Value) any {
 //     your own. Alternatively, if reacting to another user's message, you may
 //     retrieve it via the [bindings.ChannelMessageReceptionCallback] registered
 //     using  RegisterReceiveHandler (Uint8Array).
-//   - args[3] - The lease of the message. This will be how long the message is
-//     valid until, in milliseconds. As per the [channels.Manager]
-//     documentation, this has different meanings depending on the use case.
-//     These use cases may be generic enough that they will not be enumerated
-//     here (int).
+//   - args[3] - The lease of the message. This will be how long the
+//     message is available from the network, in milliseconds (int). As per the
+//     [channels.Manager] documentation, this has different meanings depending
+//     on the use case. These use cases may be generic enough that they will not
+//     be enumerated here. Use [ValidForever] to last the max message life.
 //   - args[4] - JSON of [xxdk.CMIXParams]. If left empty
 //     [bindings.GetDefaultCMixParams] will be used internally (Uint8Array).
 //
@@ -954,10 +973,11 @@ func (cm *ChannelsManager) SendReply(_ js.Value, args []js.Value) any {
 	return utils.CreatePromise(promiseFn)
 }
 
-// SendReaction is used to send a reaction to a message over a channel.
-// The reaction must be a single emoji with no other characters, and will
-// be rejected otherwise.
-// Users will drop the reaction if they do not recognize the reactTo message.
+// SendReaction is used to send a reaction to a message over a channel. The
+// reaction must be a single emoji with no other characters, and will be
+// rejected otherwise.
+//
+// Clients will drop the reaction if they do not recognize the reactTo message.
 //
 // Parameters:
 //   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
@@ -1014,11 +1034,11 @@ func (cm *ChannelsManager) SendReaction(_ js.Value, args []js.Value) any {
 //     [channels.MessageType] (int).
 //   - args[2] - The contents of the message (Uint8Array). The message should be
 //     at most 510 bytes.
-//   - args[3] - The lease of the message. This will be how long the message is
-//     valid until, in milliseconds. As per the [channels.Manager]
-//     documentation, this has different meanings depending on the use case.
-//     These use cases may be generic enough that they will not be enumerated
-//     here (int).
+//   - args[3] - The lease of the message. This will be how long the
+//     message is available from the network, in milliseconds (int). As per the
+//     [channels.Manager] documentation, this has different meanings depending
+//     on the use case. These use cases may be generic enough that they will not
+//     be enumerated here. Use [ValidForever] to last the max message life.
 //   - args[4] - Set tracked to true if the message should be tracked in the
 //     sendTracker, which allows messages to be shown locally before they are
 //     received on the network. In general, all messages that will be displayed
@@ -1065,8 +1085,7 @@ func (cm *ChannelsManager) SendAdminGeneric(_ js.Value, args []js.Value) any {
 //   - args[0] - Marshalled bytes of channel [id.ID] (Uint8Array).
 //   - args[1] - The marshalled [channel.MessageID] of the message you want to
 //     delete (Uint8Array).
-//   - args[2] - Set to true to un-delete the message (boolean).
-//   - args[3] - JSON of [xxdk.CMIXParams]. This may be empty, and
+//   - args[2] - JSON of [xxdk.CMIXParams]. This may be empty, and
 //     [GetDefaultCMixParams] will be used internally (Uint8Array).
 //
 // Returns a promise:
@@ -1075,12 +1094,11 @@ func (cm *ChannelsManager) SendAdminGeneric(_ js.Value, args []js.Value) any {
 func (cm *ChannelsManager) DeleteMessage(_ js.Value, args []js.Value) any {
 	channelIdBytes := utils.CopyBytesToGo(args[0])
 	targetMessageIdBytes := utils.CopyBytesToGo(args[1])
-	undoAction := args[2].Bool()
-	cmixParamsJSON := utils.CopyBytesToGo(args[3])
+	cmixParamsJSON := utils.CopyBytesToGo(args[2])
 
 	promiseFn := func(resolve, reject func(args ...any) js.Value) {
 		sendReport, err := cm.api.DeleteMessage(
-			channelIdBytes, targetMessageIdBytes, undoAction, cmixParamsJSON)
+			channelIdBytes, targetMessageIdBytes, cmixParamsJSON)
 		if err != nil {
 			reject(utils.JsTrace(err))
 		} else {
@@ -1091,9 +1109,9 @@ func (cm *ChannelsManager) DeleteMessage(_ js.Value, args []js.Value) any {
 	return utils.CreatePromise(promiseFn)
 }
 
-// PinMessage pins the target message to the top of a channel view for all
-// users in the specified channel. Only the channel admin can pin user
-// messages; if the user is not an admin of the channel, then the error
+// PinMessage pins the target message to the top of a channel view for all users
+// in the specified channel. Only the channel admin can pin user messages; if
+// the user is not an admin of the channel, then the error
 // [channels.NotAnAdminErr] is returned.
 //
 // If undoAction is true, then the targeted message is unpinned.
@@ -1105,7 +1123,9 @@ func (cm *ChannelsManager) DeleteMessage(_ js.Value, args []js.Value) any {
 //   - args[1] - The marshalled [channel.MessageID] of the message you want to
 //     pin (Uint8Array).
 //   - args[2] - Set to true to unpin the message (boolean).
-//   - args[3] - JSON of [xxdk.CMIXParams]. This may be empty, and
+//   - args[3] - The time, in milliseconds, that the message should be pinned
+//     (int). To remain pinned indefinitely, use [ValidForever].
+//   - args[4] - JSON of [xxdk.CMIXParams]. This may be empty, and
 //     [GetDefaultCMixParams] will be used internally (Uint8Array).
 //
 // Returns a promise:
@@ -1115,11 +1135,12 @@ func (cm *ChannelsManager) PinMessage(_ js.Value, args []js.Value) any {
 	channelIdBytes := utils.CopyBytesToGo(args[0])
 	targetMessageIdBytes := utils.CopyBytesToGo(args[1])
 	undoAction := args[2].Bool()
-	cmixParamsJSON := utils.CopyBytesToGo(args[3])
+	validUntilMS := args[3].Int()
+	cmixParamsJSON := utils.CopyBytesToGo(args[4])
 
 	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		sendReport, err := cm.api.PinMessage(
-			channelIdBytes, targetMessageIdBytes, undoAction, cmixParamsJSON)
+		sendReport, err := cm.api.PinMessage(channelIdBytes,
+			targetMessageIdBytes, undoAction, validUntilMS, cmixParamsJSON)
 		if err != nil {
 			reject(utils.JsTrace(err))
 		} else {
@@ -1131,10 +1152,10 @@ func (cm *ChannelsManager) PinMessage(_ js.Value, args []js.Value) any {
 }
 
 // MuteUser is used to mute a user in a channel. Muting a user will cause all
-// future messages from the user being hidden from view. Muted users are also
-// unable to send messages. Only the channel admin can mute a user; if the user
-// is not an admin of the channel, then the error [channels.NotAnAdminErr] is
-// returned.
+// future messages from the user being dropped on reception. Muted users are
+// also unable to send messages. Only the channel admin can mute a user; if the
+// user is not an admin of the channel, then the error [channels.NotAnAdminErr]
+// is returned.
 //
 // If undoAction is true, then the targeted user will be unmuted.
 //
@@ -1143,7 +1164,9 @@ func (cm *ChannelsManager) PinMessage(_ js.Value, args []js.Value) any {
 //   - args[1] - The [ed25519.PublicKey] of the user you want to mute
 //     (Uint8Array).
 //   - args[2] - Set to true to unmute the message (boolean).
-//   - args[3] - JSON of [xxdk.CMIXParams]. This may be empty, and
+//   - args[3] - The time, in milliseconds, that the user should be muted (int).
+//     To remain muted indefinitely, use [ValidForever].
+//   - args[4] - JSON of [xxdk.CMIXParams]. This may be empty, and
 //     [GetDefaultCMixParams] will be used internally (Uint8Array).
 //
 // Returns a promise:
@@ -1153,11 +1176,12 @@ func (cm *ChannelsManager) MuteUser(_ js.Value, args []js.Value) any {
 	channelIdBytes := utils.CopyBytesToGo(args[0])
 	mutedUserPubKeyBytes := utils.CopyBytesToGo(args[1])
 	undoAction := args[2].Bool()
-	cmixParamsJSON := utils.CopyBytesToGo(args[3])
+	validUntilMS := args[3].Int()
+	cmixParamsJSON := utils.CopyBytesToGo(args[4])
 
 	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		sendReport, err := cm.api.MuteUser(
-			channelIdBytes, mutedUserPubKeyBytes, undoAction, cmixParamsJSON)
+		sendReport, err := cm.api.MuteUser(channelIdBytes, mutedUserPubKeyBytes,
+			undoAction, validUntilMS, cmixParamsJSON)
 		if err != nil {
 			reject(utils.JsTrace(err))
 		} else {
@@ -1172,8 +1196,8 @@ func (cm *ChannelsManager) MuteUser(_ js.Value, args []js.Value) any {
 // Other Channel Actions                                                      //
 ////////////////////////////////////////////////////////////////////////////////
 
-// GetIdentity returns the marshaled public identity ([channel.Identity]) that
-// the channel is using.
+// GetIdentity returns the public identity ([channel.Identity]) of the user
+// associated with this channel manager.
 //
 // Returns:
 //   - JSON of the [channel.Identity] (Uint8Array).
@@ -1188,14 +1212,14 @@ func (cm *ChannelsManager) GetIdentity(js.Value, []js.Value) any {
 	return utils.CopyBytesToJS(i)
 }
 
-// ExportPrivateIdentity encrypts and exports the private identity to a portable
-// string.
+// ExportPrivateIdentity encrypts the private identity using the password and
+// exports it to a portable string.
 //
 // Parameters:
-//   - args[0] - Password to encrypt the identity with (string).
+//   - password - The password used to encrypt the private identity (string).
 //
 // Returns:
-//   - JSON of the encrypted private identity (Uint8Array).
+//   - Encrypted portable private identity (Uint8Array).
 //   - Throws TypeError if exporting the identity fails.
 func (cm *ChannelsManager) ExportPrivateIdentity(_ js.Value, args []js.Value) any {
 	i, err := cm.api.ExportPrivateIdentity(args[0].String())
@@ -1207,7 +1231,8 @@ func (cm *ChannelsManager) ExportPrivateIdentity(_ js.Value, args []js.Value) an
 	return utils.CopyBytesToJS(i)
 }
 
-// GetStorageTag returns the storage tag needed to reload the manager.
+// GetStorageTag returns the tag where this manager is stored. To be used when
+// loading the manager. The storage tag is derived from the public key.
 //
 // Returns:
 //   - Storage tag (string).
@@ -1235,7 +1260,8 @@ func (cm *ChannelsManager) SetNickname(_ js.Value, args []js.Value) any {
 	return nil
 }
 
-// DeleteNickname deletes the nickname for a given channel.
+// DeleteNickname removes the nickname for a given channel. The name will revert
+// back to the codename for this channel instead.
 //
 // Parameters:
 //   - args[0] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
@@ -1259,7 +1285,7 @@ func (cm *ChannelsManager) DeleteNickname(_ js.Value, args []js.Value) any {
 //   - args[0] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
 //
 // Returns:
-//   - The nickname (string).
+//   - The nickname set for the channel (string).
 //   - Throws TypeError if the channel has no nickname set.
 func (cm *ChannelsManager) GetNickname(_ js.Value, args []js.Value) any {
 	nickname, err := cm.api.GetNickname(utils.CopyBytesToGo(args[0]))
@@ -1321,7 +1347,7 @@ func (cm *ChannelsManager) Muted(_ js.Value, args []js.Value) any {
 //   - args[0] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
 //
 // Returns:
-//   - []byte - JSON of []ed25519.PublicKey (Uint8Array). Look below for an
+//   - JSON of an array of ed25519.PublicKey (Uint8Array). Look below for an
 //     example.
 //   - Throws a TypeError if the channel ID cannot be unmarshalled.
 //
@@ -1415,9 +1441,9 @@ func (cm *ChannelsManager) ExportChannelAdminKey(_ js.Value, args []js.Value) an
 // Returns:
 //   - bool - True if the private key belongs to the channel and false
 //     otherwise.
-//   - Throws a TypeError with the message [Channels.WrongPasswordErr] for an
+//   - Throws a TypeError with the message [channels.WrongPasswordErr] for an
 //     invalid password.
-//   - Throws a TypeError with the message [Channels.ChannelDoesNotExistsErr] i
+//   - Throws a TypeError with the message [channels.ChannelDoesNotExistsErr] i
 //     the channel has not already been joined.
 func (cm *ChannelsManager) VerifyChannelAdminKey(_ js.Value, args []js.Value) any {
 	channelID := utils.CopyBytesToGo(args[0])
@@ -1446,11 +1472,11 @@ func (cm *ChannelsManager) VerifyChannelAdminKey(_ js.Value, args []js.Value) an
 // Returns:
 //   - Throws a TypeError if the password is invalid or the private key does
 //     not match the channel ID.
-//   - Throws a TypeError with the message [Channels.WrongPasswordErr] for an
+//   - Throws a TypeError with the message [channels.WrongPasswordErr] for an
 //     invalid password.
-//   - Throws a TypeError with the message [Channels.ChannelDoesNotExistsErr] if
+//   - Throws a TypeError with the message [channels.ChannelDoesNotExistsErr] if
 //     the channel has not already been joined.
-//   - Throws a TypeError with the message [Channels.WrongPrivateKeyErr] if the
+//   - Throws a TypeError with the message [channels.WrongPrivateKeyErr] if the
 //     private key does not belong to the channel.
 func (cm *ChannelsManager) ImportChannelAdminKey(_ js.Value, args []js.Value) any {
 	channelID := utils.CopyBytesToGo(args[0])
@@ -1581,6 +1607,7 @@ func (emb *eventModelBuilder) Build(path string) bindings.EventModel {
 		updateFromUUID:      utils.WrapCB(emJs, "UpdateFromUUID"),
 		updateFromMessageID: utils.WrapCB(emJs, "UpdateFromMessageID"),
 		getMessage:          utils.WrapCB(emJs, "GetMessage"),
+		deleteMessage:       utils.WrapCB(emJs, "DeleteMessage"),
 	}
 }
 
@@ -1595,6 +1622,7 @@ type eventModel struct {
 	updateFromUUID      func(args ...any) js.Value
 	updateFromMessageID func(args ...any) js.Value
 	getMessage          func(args ...any) js.Value
+	deleteMessage       func(args ...any) js.Value
 }
 
 // JoinChannel is called whenever a channel is joined locally.
@@ -1790,7 +1818,25 @@ func (em *eventModel) GetMessage(messageID []byte) ([]byte, error) {
 		return nil, err
 	}
 
-	return utils.CopyBytesToGo(em.getMessage(utils.CopyBytesToJS(messageID))), nil
+	if mae.Error != "" {
+		return nil, errors.New(mae.Error)
+	}
+
+	return json.Marshal(mae.ModelMessage)
+}
+
+// DeleteMessage deletes the message with the given [channel.MessageID] from
+// the database.
+//
+// Parameters:
+//  - messageID - The bytes of the [channel.MessageID] of the message.
+func (em *eventModel) DeleteMessage(messageID []byte) error {
+	err := em.deleteMessage(utils.CopyBytesToJS(messageID))
+	if !err.IsUndefined() {
+		return js.Error{Value: err}
+	}
+
+	return nil
 }
 
 // MessageAndError contains a message returned by eventModel.GetMessage or any
@@ -1820,7 +1866,7 @@ func (em *eventModel) GetMessage(messageID []byte) ([]byte, error) {
 //	  "Error": ""
 //	}
 type MessageAndError struct {
-	// MessageJSON should contain the JSON of [channels.ModelMessage].
+	// MessageJSON should contain the JSON of channels.ModelMessage.
 	ModelMessage channels.ModelMessage
 
 	// Error should only be filled when an error occurs on message lookup.
diff --git a/wasm/connect.go b/wasm/connect.go
index 5d8e347dbb34c9adf4f724eee13a3eda43fc4f57..bfb95a6d882f808277a856dfbda9937c3bb3d687 100644
--- a/wasm/connect.go
+++ b/wasm/connect.go
@@ -80,10 +80,6 @@ func (c *Cmix) Connect(_ js.Value, args []js.Value) any {
 // SendE2E is a wrapper for sending specifically to the [Connection]'s
 // [partner.Manager].
 //
-// Returns:
-//   - []byte - the JSON marshalled bytes of the E2ESendReport object, which can
-//     be passed into [Cmix.WaitForRoundResult] to see if the send succeeded.
-//
 // Parameters:
 //   - args[0] - Message type from [catalog.MessageType] (int).
 //   - args[1] - Message payload (Uint8Array).