diff --git a/go.mod b/go.mod
index 138480784216ade62e2b04ea5ff3458733070894..92c0dc445041dac91e91b60ac47ce7211d808a2f 100644
--- a/go.mod
+++ b/go.mod
@@ -7,10 +7,10 @@ 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 v1.5.1-0.20221121234059-3f75d507e3c8
+	gitlab.com/elixxir/client v1.5.1-0.20221122004847-08fb6e6d3c0e
 	gitlab.com/elixxir/crypto v0.0.7-0.20221121233335-83f145891bc7
-	gitlab.com/elixxir/primitives v0.0.3-0.20221110181119-e83320a48b13
-	gitlab.com/xx_network/crypto v0.0.5-0.20221110181048-76f0c556fe95
+	gitlab.com/elixxir/primitives v0.0.3-0.20221114231218-cc461261a6af
+	gitlab.com/xx_network/crypto v0.0.5-0.20221121220724-8eefdbb0eb46
 	gitlab.com/xx_network/primitives v0.0.4-0.20221110180011-fd6ea3058225
 	golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
 )
diff --git a/go.sum b/go.sum
index 4fcea68df113835b64e255ed24597200921f1e0a..6faf0c78b4776e56073db5e1688a70cb4944f509 100644
--- a/go.sum
+++ b/go.sum
@@ -368,20 +368,20 @@ 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 v1.5.1-0.20221121234059-3f75d507e3c8 h1:DWzodsczpQh9Y5415grILfP/xd0ol0mKczUyTJ5Gp0k=
-gitlab.com/elixxir/client v1.5.1-0.20221121234059-3f75d507e3c8/go.mod h1:ZUvPwnyqsvQaiZCkWKb4coWa5nwv6XxRthV/64rZK4s=
+gitlab.com/elixxir/client v1.5.1-0.20221122004847-08fb6e6d3c0e h1:HZuKqQqgwKjXzMVY3/n0nlAd/+0aFRvJKD4/LIrNEew=
+gitlab.com/elixxir/client v1.5.1-0.20221122004847-08fb6e6d3c0e/go.mod h1:ZUvPwnyqsvQaiZCkWKb4coWa5nwv6XxRthV/64rZK4s=
 gitlab.com/elixxir/comms v0.0.4-0.20221110181420-84bca6216fe4 h1:bLRjVCyMVde4n2hTVgoyyIAWrKI4CevpChchkPeb6A0=
 gitlab.com/elixxir/comms v0.0.4-0.20221110181420-84bca6216fe4/go.mod h1:XhI2/CMng+xcH3mAs+1aPz29PSNu1079XMJ8V+xxihw=
 gitlab.com/elixxir/crypto v0.0.7-0.20221121233335-83f145891bc7 h1:yhc8jQ27JKypdRE41NpfJPaYRS0sNkOwugaIyoscDiU=
 gitlab.com/elixxir/crypto v0.0.7-0.20221121233335-83f145891bc7/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.20221110181119-e83320a48b13 h1:U3tbClFN5BLYlAoMj+o6VWAs9akbFiJstMGCuk1aB94=
-gitlab.com/elixxir/primitives v0.0.3-0.20221110181119-e83320a48b13/go.mod h1:DUnCTXYKgjpro5+6ITySKIf+qzW2vhW40IVHMimdsqw=
+gitlab.com/elixxir/primitives v0.0.3-0.20221114231218-cc461261a6af h1:xcPqknK1ehNb9xwcutTdoR0YgD7DC/ySh9z49tIpSxQ=
+gitlab.com/elixxir/primitives v0.0.3-0.20221114231218-cc461261a6af/go.mod h1:DUnCTXYKgjpro5+6ITySKIf+qzW2vhW40IVHMimdsqw=
 gitlab.com/xx_network/comms v0.0.4-0.20221110181111-4f0694876936 h1:eQQ4zUvGWIzCWdBJ6qlysWUMwrc2tM8GripFqdT1SAs=
 gitlab.com/xx_network/comms v0.0.4-0.20221110181111-4f0694876936/go.mod h1:+RfHgk75ywMvmucOpPS7rSUlsnbPyBuLsr13tsthUTE=
-gitlab.com/xx_network/crypto v0.0.5-0.20221110181048-76f0c556fe95 h1:rC6lx6sD6u617Qu0ZndKZQRjXuRkyrI9Q6Y0Ki+dnK4=
-gitlab.com/xx_network/crypto v0.0.5-0.20221110181048-76f0c556fe95/go.mod h1:acWUBKCpae/XVaQF7J9RnLAlBT13i5r7gnON+mrIxBk=
+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.20221110180011-fd6ea3058225 h1:TAn87e6Zt9KwcSnWKyIul5eu8T0RHY9FDubCGs3G0dw=
 gitlab.com/xx_network/primitives v0.0.4-0.20221110180011-fd6ea3058225/go.mod h1:rP/2IsqIFHapuIB4mstXKItvwoJRQ9Wlms/NGeutHsk=
 gitlab.com/xx_network/ring v0.0.3-0.20220902183151-a7d3b15bc981 h1:1s0vX9BbkiD0IVXwr3LOaTBcq1wBrWcUWMBK0s8r0Z0=
diff --git a/indexedDb/implementation.go b/indexedDb/implementation.go
index 59192d2658b3c5efbc488b60720e857d6cc8a23f..5cd3503b810fb08f8958bdaa22576bcdb53de385 100644
--- a/indexedDb/implementation.go
+++ b/indexedDb/implementation.go
@@ -205,7 +205,7 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID,
 	messageID cryptoChannel.MessageID, nickname, text string,
 	pubKey ed25519.PublicKey, codeset uint8,
 	timestamp time.Time, lease time.Duration, round rounds.Round,
-	mType channels.MessageType, status channels.SentStatus) uint64 {
+	mType channels.MessageType, status channels.SentStatus, hidden bool) uint64 {
 	textBytes := []byte(text)
 	var err error
 
@@ -220,7 +220,8 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID,
 
 	msgToInsert := buildMessage(
 		channelID.Marshal(), messageID.Bytes(), nil, nickname,
-		textBytes, pubKey, codeset, timestamp, lease, round.ID, mType, status)
+		textBytes, pubKey, codeset, timestamp, lease, round.ID, mType,
+		false, hidden, status)
 
 	uuid, err := w.receiveHelper(msgToInsert, false)
 	if err != nil {
@@ -241,7 +242,7 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID,
 	messageID cryptoChannel.MessageID, replyTo cryptoChannel.MessageID,
 	nickname, text string, pubKey ed25519.PublicKey, codeset uint8,
 	timestamp time.Time, lease time.Duration, round rounds.Round,
-	mType channels.MessageType, status channels.SentStatus) uint64 {
+	mType channels.MessageType, status channels.SentStatus, hidden bool) uint64 {
 	textBytes := []byte(text)
 	var err error
 
@@ -256,7 +257,7 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID,
 
 	msgToInsert := buildMessage(channelID.Marshal(), messageID.Bytes(),
 		replyTo.Bytes(), nickname, textBytes, pubKey, codeset, timestamp, lease,
-		round.ID, mType, status)
+		round.ID, mType, false, hidden, status)
 
 	uuid, err := w.receiveHelper(msgToInsert, false)
 
@@ -277,7 +278,7 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID,
 	messageID cryptoChannel.MessageID, reactionTo cryptoChannel.MessageID,
 	nickname, reaction string, pubKey ed25519.PublicKey, codeset uint8,
 	timestamp time.Time, lease time.Duration, round rounds.Round,
-	mType channels.MessageType, status channels.SentStatus) uint64 {
+	mType channels.MessageType, status channels.SentStatus, hidden bool) uint64 {
 	textBytes := []byte(reaction)
 	var err error
 
@@ -292,7 +293,8 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID,
 
 	msgToInsert := buildMessage(
 		channelID.Marshal(), messageID.Bytes(), reactionTo.Bytes(), nickname,
-		textBytes, pubKey, codeset, timestamp, lease, round.ID, mType, status)
+		textBytes, pubKey, codeset, timestamp, lease, round.ID, mType,
+		false, hidden, status)
 
 	uuid, err := w.receiveHelper(msgToInsert, false)
 	if err != nil {
@@ -302,15 +304,52 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID,
 	return uuid
 }
 
-// UpdateSentStatus is called whenever the [channels.SentStatus] of a message
-// has changed. At this point the message ID goes from empty/unknown to
-// populated.
+// UpdateFromMessageID is called whenever a message with the message ID is
+// modified.
 //
-// TODO: Potential race condition due to separate get/update operations.
-func (w *wasmModel) UpdateSentStatus(uuid uint64,
-	messageID cryptoChannel.MessageID, timestamp time.Time, round rounds.Round,
-	status channels.SentStatus) {
-	parentErr := errors.New("failed to UpdateSentStatus")
+// The API needs to return the UUID of the modified message that can be
+// referenced at a later time.
+//
+// timestamp, round, pinned, and hidden are all nillable and may be updated
+// based upon the UUID at a later date. If a nil value is passed, then make
+// no update.
+func (w *wasmModel) UpdateFromMessageID(messageID cryptoChannel.MessageID,
+	timestamp *time.Time, round *rounds.Round, pinned, hidden *bool,
+	status *channels.SentStatus) uint64 {
+	parentErr := errors.New("failed to UpdateFromMessageID")
+
+	// FIXME: this is a bit of race condition without the mux.
+	//        This should be done via the transactions (i.e., make a
+	//        special version of receiveHelper)
+	w.updateMux.Lock()
+	defer w.updateMux.Unlock()
+
+	msgIDStr := base64.StdEncoding.EncodeToString(messageID.Marshal())
+	currentMsgObj, err := w.getIndex(messageStoreName,
+		messageStoreMessageIndex, js.ValueOf(msgIDStr))
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Failed to get message by index: %+v", err))
+		return 0
+	}
+
+	currentMsg := utils.JsToJson(currentMsgObj)
+	uuid, err := w.updateMessage(currentMsg, &messageID, timestamp,
+		round, pinned, hidden, status)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to updateMessage: %+v", err))
+	}
+	return uuid
+}
+
+// messageID, timestamp, round, pinned, and hidden are all nillable and may
+// be updated based upon the UUID at a later date. If a nil value is passed,
+// then make no update.
+func (w *wasmModel) UpdateFromUUID(uuid uint64, messageID *cryptoChannel.MessageID,
+	timestamp *time.Time, round *rounds.Round, pinned, hidden *bool,
+	status *channels.SentStatus) {
+	parentErr := errors.New("failed to UpdateFromUUID")
 
 	// FIXME: this is a bit of race condition without the mux.
 	//        This should be done via the transactions (i.e., make a
@@ -324,36 +363,64 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64,
 	// Use the key to get the existing Message
 	currentMsg, err := w.get(messageStoreName, key)
 	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Failed to get message: %+v", err))
 		return
 	}
 
-	// Extract the existing Message and update the Status
+	_, err = w.updateMessage(currentMsg, messageID, timestamp,
+		round, pinned, hidden, status)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to updateMessage: %+v", err))
+	}
+}
+
+// updateMessage is a helper for updating a stored message.
+func (w *wasmModel) updateMessage(currentMsgJson string,
+	messageID *cryptoChannel.MessageID, timestamp *time.Time,
+	round *rounds.Round, pinned, hidden *bool,
+	status *channels.SentStatus) (uint64, error) {
+
 	newMessage := &Message{}
-	err = json.Unmarshal([]byte(currentMsg), newMessage)
+	err := json.Unmarshal([]byte(currentMsgJson), newMessage)
 	if err != nil {
-		return
+		return 0, nil
 	}
-	newMessage.Status = uint8(status)
-	if !messageID.Equals(cryptoChannel.MessageID{}) {
-		newMessage.MessageID = messageID.Bytes()
+
+	if status != nil {
+		newMessage.Status = uint8(*status)
+	}
+	if messageID != nil {
+		newMessage.MessageID = messageID.Marshal()
 	}
 
-	if round.ID != 0 {
+	if round != nil {
 		newMessage.Round = uint64(round.ID)
 	}
 
-	if !timestamp.Equal(time.Time{}) {
-		newMessage.Timestamp = timestamp
+	if timestamp != nil {
+		newMessage.Timestamp = *timestamp
+	}
+
+	if pinned != nil {
+		newMessage.Pinned = *pinned
+	}
+
+	if hidden != nil {
+		newMessage.Hidden = *hidden
 	}
 
 	// Store the updated Message
-	_, err = w.receiveHelper(newMessage, true)
+	uuid, err := w.receiveHelper(newMessage, true)
 	if err != nil {
-		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
+		return 0, err
 	}
 	channelID := &id.ID{}
 	copy(channelID[:], newMessage.ChannelID)
 	go w.receivedMessageCB(uuid, channelID, true)
+
+	return uuid, nil
 }
 
 // buildMessage is a private helper that converts typical [channels.EventModel]
@@ -365,7 +432,7 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64,
 func buildMessage(channelID, messageID, parentID []byte, nickname string,
 	text []byte, pubKey ed25519.PublicKey, codeset uint8, timestamp time.Time,
 	lease time.Duration, round id.Round, mType channels.MessageType,
-	status channels.SentStatus) *Message {
+	pinned, hidden bool, status channels.SentStatus) *Message {
 	return &Message{
 		MessageID:       messageID,
 		Nickname:        nickname,
@@ -374,8 +441,8 @@ func buildMessage(channelID, messageID, parentID []byte, nickname string,
 		Timestamp:       timestamp,
 		Lease:           lease,
 		Status:          uint8(status),
-		Hidden:          false,
-		Pinned:          false,
+		Hidden:          hidden,
+		Pinned:          pinned,
 		Text:            text,
 		Type:            uint16(mType),
 		Round:           uint64(round),
@@ -435,9 +502,9 @@ func (w *wasmModel) receiveHelper(newMessage *Message, isUpdate bool) (uint64,
 	if res.IsUndefined() {
 		msgID := cryptoChannel.MessageID{}
 		copy(msgID[:], newMessage.MessageID)
-		uuid, errLookup := w.msgIDLookup(msgID)
-		if uuid != 0 && errLookup == nil {
-			return uuid, nil
+		msg, errLookup := w.msgIDLookup(msgID)
+		if msg.ID != 0 && errLookup == nil {
+			return msg.ID, nil
 		}
 		return 0, errors.Errorf("uuid lookup failure: %+v", err)
 	}
@@ -447,6 +514,48 @@ func (w *wasmModel) receiveHelper(newMessage *Message, isUpdate bool) (uint64,
 	return uuid, nil
 }
 
+// GetMessage returns the message with the given [channel.MessageID].
+func (w *wasmModel) GetMessage(messageID cryptoChannel.MessageID) (channels.ModelMessage, error) {
+	lookupResult, err := w.msgIDLookup(messageID)
+	if err != nil {
+		return channels.ModelMessage{}, err
+	}
+
+	var channelId *id.ID
+	if lookupResult.ChannelID != nil {
+		channelId, err = id.Unmarshal(lookupResult.ChannelID)
+		if err != nil {
+			return channels.ModelMessage{}, err
+		}
+	}
+
+	var parentMsgId cryptoChannel.MessageID
+	if lookupResult.ParentMessageID != nil {
+		parentMsgId, err = cryptoChannel.UnmarshalMessageID(lookupResult.ParentMessageID)
+		if err != nil {
+			return channels.ModelMessage{}, err
+		}
+	}
+
+	return channels.ModelMessage{
+		UUID:            lookupResult.ID,
+		Nickname:        lookupResult.Nickname,
+		MessageID:       messageID,
+		ChannelID:       channelId,
+		ParentMessageID: parentMsgId,
+		Timestamp:       lookupResult.Timestamp,
+		Lease:           lookupResult.Lease,
+		Status:          channels.SentStatus(lookupResult.Status),
+		Hidden:          lookupResult.Hidden,
+		Pinned:          lookupResult.Pinned,
+		Content:         lookupResult.Text,
+		Type:            channels.MessageType(lookupResult.Type),
+		Round:           id.Round(lookupResult.Round),
+		PubKey:          lookupResult.Pubkey,
+		CodesetVersion:  lookupResult.CodesetVersion,
+	}, nil
+}
+
 // get is a generic private helper for getting values from the given
 // [idb.ObjectStore].
 func (w *wasmModel) get(objectStoreName string, key js.Value) (string, error) {
@@ -486,54 +595,70 @@ func (w *wasmModel) get(objectStoreName string, key js.Value) (string, error) {
 	return resultStr, nil
 }
 
-func (w *wasmModel) msgIDLookup(messageID cryptoChannel.MessageID) (uint64,
-	error) {
-	parentErr := errors.Errorf("failed to get %s/%s", messageStoreName,
-		messageID)
+// getIndex is a generic private helper for getting values from the given
+// [idb.ObjectStore] using a [idb.Index].
+func (w *wasmModel) getIndex(objectStoreName string,
+	indexName string, key js.Value) (js.Value, error) {
+
+	parentErr := errors.Errorf("failed to getIndex %s/%s/%s",
+		objectStoreName, indexName, key)
 
 	// Prepare the Transaction
-	txn, err := w.db.Transaction(idb.TransactionReadOnly, messageStoreName)
+	txn, err := w.db.Transaction(idb.TransactionReadOnly, objectStoreName)
 	if err != nil {
-		return 0, errors.WithMessagef(parentErr,
+		return js.Null(), errors.WithMessagef(parentErr,
 			"Unable to create Transaction: %+v", err)
 	}
-	store, err := txn.ObjectStore(messageStoreName)
+	store, err := txn.ObjectStore(objectStoreName)
 	if err != nil {
-		return 0, errors.WithMessagef(parentErr,
+		return js.Null(), errors.WithMessagef(parentErr,
 			"Unable to get ObjectStore: %+v", err)
 	}
-	idx, err := store.Index(messageStoreMessageIndex)
+	idx, err := store.Index(indexName)
 	if err != nil {
-		return 0, errors.WithMessagef(parentErr,
+		return js.Null(), errors.WithMessagef(parentErr,
 			"Unable to get index: %+v", err)
 	}
 
-	msgIDStr := base64.StdEncoding.EncodeToString(messageID.Bytes())
-
-	keyReq, err := idx.Get(js.ValueOf(msgIDStr))
+	// Perform the operation
+	getRequest, err := idx.Get(key)
 	if err != nil {
-		return 0, errors.WithMessagef(parentErr,
-			"Unable to get keyReq: %+v", err)
+		return js.Null(), errors.WithMessagef(parentErr,
+			"Unable to Get from ObjectStore: %+v", err)
 	}
+
 	// Wait for the operation to return
 	ctx, cancel := newContext()
-	keyObj, err := keyReq.Await(ctx)
+	resultObj, err := getRequest.Await(ctx)
 	cancel()
 	if err != nil {
-		return 0, errors.WithMessagef(parentErr,
+		return js.Null(), errors.WithMessagef(parentErr,
 			"Unable to get from ObjectStore: %+v", err)
 	}
 
-	// Process result into string
-	resultStr := utils.JsToJson(keyObj)
-	jww.DEBUG.Printf("Index lookup of %s/%s/%s: %s", messageStoreName,
-		messageStoreMessageIndex, msgIDStr, resultStr)
+	jww.DEBUG.Printf("Got via index from %s/%s/%s: %s",
+		objectStoreName, indexName, key, resultObj.String())
+	return resultObj, nil
+}
 
-	uuid := uint64(0)
-	if !keyObj.IsUndefined() {
-		uuid = uint64(keyObj.Get("id").Int())
+func (w *wasmModel) msgIDLookup(messageID cryptoChannel.MessageID) (*Message,
+	error) {
+	msgIDStr := base64.StdEncoding.EncodeToString(messageID.Marshal())
+	keyObj, err := w.getIndex(messageStoreName,
+		messageStoreMessageIndex, js.ValueOf(msgIDStr))
+	if err != nil {
+		return nil, err
+	} else if keyObj.IsUndefined() {
+		return nil, errors.Errorf("no message for %s found", msgIDStr)
 	}
-	return uuid, nil
+
+	// Process result into string
+	resultMsg := &Message{}
+	err = json.Unmarshal([]byte(utils.JsToJson(keyObj)), resultMsg)
+	if err != nil {
+		return nil, err
+	}
+	return resultMsg, nil
 }
 
 // dump returns the given [idb.ObjectStore] contents to string slice for
diff --git a/indexedDb/implementation_test.go b/indexedDb/implementation_test.go
index d4f6226f956b1547e17100062db2b6f603df42d0..8cff2ba6bce794fb8e0dd79ed45d37a6f58fd0b9 100644
--- a/indexedDb/implementation_test.go
+++ b/indexedDb/implementation_test.go
@@ -47,7 +47,7 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
 	// Store a test message
 	testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
 		testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(),
-		time.Second, 0, 0, channels.Sent)
+		time.Second, 0, 0, false, false, channels.Sent)
 	uuid, err := eventModel.receiveHelper(testMsg, false)
 	if err != nil {
 		t.Fatalf("%+v", err)
@@ -64,8 +64,8 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
 
 	// Update the sentStatus
 	expectedStatus := channels.Failed
-	eventModel.UpdateSentStatus(uuid, testMsgId, netTime.Now(),
-		rounds.Round{ID: 8675309}, expectedStatus)
+	eventModel.UpdateFromUUID(uuid, nil, nil,
+		nil, nil, nil, &expectedStatus)
 
 	// Check the resulting status
 	results, err = eventModel.dump(messageStoreName)
@@ -147,7 +147,7 @@ func Test_wasmModel_UUIDTest(t *testing.T) {
 		rnd := rounds.Round{ID: id.Round(42)}
 		uuid := eventModel.ReceiveMessage(channelID, msgID, "test",
 			testString+fmt.Sprintf("%d", i), []byte{8, 6, 7, 5}, 0,
-			netTime.Now(), time.Hour, rnd, 0, channels.Sent)
+			netTime.Now(), time.Hour, rnd, 0, channels.Sent, false)
 		uuids[i] = uuid
 	}
 
@@ -182,7 +182,7 @@ func Test_wasmModel_DuplicateReceives(t *testing.T) {
 		rnd := rounds.Round{ID: id.Round(42)}
 		uuid := eventModel.ReceiveMessage(channelID, msgID, "test",
 			testString+fmt.Sprintf("%d", i), []byte{8, 6, 7, 5}, 0,
-			netTime.Now(), time.Hour, rnd, 0, channels.Sent)
+			netTime.Now(), time.Hour, rnd, 0, channels.Sent, false)
 		uuids[i] = uuid
 	}
 
@@ -226,7 +226,7 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) {
 		testMsgId := channel.MakeMessageID([]byte(testStr), &id.ID{1})
 		eventModel.ReceiveMessage(thisChannel, testMsgId, testStr, testStr,
 			[]byte{8, 6, 7, 5}, 0, netTime.Now(), time.Second,
-			rounds.Round{ID: id.Round(0)}, 0, channels.Sent)
+			rounds.Round{ID: id.Round(0)}, 0, channels.Sent, false)
 	}
 
 	// Check pre-results
@@ -286,7 +286,7 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
 	testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1})
 	testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
 		testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(),
-		time.Second, 0, 0, channels.Sent)
+		time.Second, 0, 0, false, false, channels.Sent)
 	_, err = eventModel.receiveHelper(testMsg, false)
 	if err != nil {
 		t.Fatal(err)
@@ -309,7 +309,7 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
 	testMsgId2 := channel.MakeMessageID([]byte(testString), &id.ID{2})
 	testMsg = buildMessage([]byte(testString), testMsgId2.Bytes(), nil,
 		testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(),
-		time.Second, 0, 0, channels.Sent)
+		time.Second, 0, 0, false, false, channels.Sent)
 	primaryKey, err := eventModel.receiveHelper(testMsg, false)
 	if err != nil {
 		t.Fatal(err)
diff --git a/wasm/channels.go b/wasm/channels.go
index 5eb4e3c7044e6f2cd77f17be2a977167e8b17eef..ce831a27b26008cb438540d3a4da4fc5fb2a6d0b 100644
--- a/wasm/channels.go
+++ b/wasm/channels.go
@@ -1,6 +1,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Copyright © 2022 xx foundation                                             //
-//                                                                            //
+//                                                                           //
 // Use of this source code is governed by a license that can be found in the  //
 // LICENSE file.                                                              //
 ////////////////////////////////////////////////////////////////////////////////
@@ -53,12 +53,16 @@ func newChannelsManagerJS(api *bindings.ChannelsManager) map[string]interface{}
 		"SendMessage":           js.FuncOf(cm.SendMessage),
 		"SendReply":             js.FuncOf(cm.SendReply),
 		"SendReaction":          js.FuncOf(cm.SendReaction),
+		"DeleteMessage":         js.FuncOf(cm.DeleteMessage),
+		"PinMessage":            js.FuncOf(cm.PinMessage),
+		"MuteUser":              js.FuncOf(cm.MuteUser),
 		"GetIdentity":           js.FuncOf(cm.GetIdentity),
 		"ExportPrivateIdentity": js.FuncOf(cm.ExportPrivateIdentity),
 		"GetStorageTag":         js.FuncOf(cm.GetStorageTag),
 		"SetNickname":           js.FuncOf(cm.SetNickname),
 		"DeleteNickname":        js.FuncOf(cm.DeleteNickname),
 		"GetNickname":           js.FuncOf(cm.GetNickname),
+		"Muted":                 js.FuncOf(cm.Muted),
 
 		// Channel Receiving Logic and Callback Registration
 		"RegisterReceiveHandler": js.FuncOf(cm.RegisterReceiveHandler),
@@ -1105,6 +1109,122 @@ func (ch *ChannelsManager) SendReaction(_ js.Value, args []js.Value) interface{}
 	return utils.CreatePromise(promiseFn)
 }
 
+// DeleteMessage 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.
+//
+// Parameters:
+//   - args[0] - The PEM-encoded admin RSA private key for the channel
+//     (Uint8Array). If a user is trying to delete their own message, make this
+//     empty.
+//   - args[1] - Marshalled bytes of channel [id.ID] (Uint8Array).
+//   - args[2] - The marshalled [channel.MessageID] of the message you want to
+//     delete (Uint8Array).
+//   - args[3] - Set to true to un-delete the message (boolean).
+//   - args[4] - JSON of [xxdk.CMIXParams]. This may be empty, and
+//     [GetDefaultCMixParams] will be used internally (Uint8Array).
+//
+// Returns:
+//   - Resolves to the JSON of [bindings.ChannelSendReport] (Uint8Array).
+//   - Rejected with an error if sending fails.
+func (ch *ChannelsManager) DeleteMessage(_ js.Value, args []js.Value) interface{} {
+	adminPrivateKey := utils.CopyBytesToGo(args[0])
+	channelIdBytes := utils.CopyBytesToGo(args[1])
+	targetMessageIdBytes := utils.CopyBytesToGo(args[2])
+	undoAction := args[3].Bool()
+	cmixParamsJSON := utils.CopyBytesToGo(args[4])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		sendReport, err := ch.api.DeleteMessage(adminPrivateKey, channelIdBytes,
+			targetMessageIdBytes, undoAction, cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(sendReport))
+		}
+	}
+
+	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.
+//
+// Clients will drop the pin if they do not recognize the target message.
+//
+// Parameters:
+//   - args[0] - The PEM-encoded admin RSA private key for the channel
+//     (Uint8Array).
+//   - args[1] - Marshalled bytes of channel [id.ID] (Uint8Array).
+//   - args[2] - The marshalled [channel.MessageID] of the message you want to
+//     pin (Uint8Array).
+//   - args[3] - Set to true to un-delete the message (boolean).
+//   - args[4] - JSON of [xxdk.CMIXParams]. This may be empty, and
+//     [GetDefaultCMixParams] will be used internally (Uint8Array).
+//
+// Returns:
+//   - []byte - JSON of [ChannelSendReport].
+func (ch *ChannelsManager) PinMessage(_ js.Value, args []js.Value) interface{} {
+	adminPrivateKey := utils.CopyBytesToGo(args[0])
+	channelIdBytes := utils.CopyBytesToGo(args[1])
+	targetMessageIdBytes := utils.CopyBytesToGo(args[2])
+	undoAction := args[3].Bool()
+	cmixParamsJSON := utils.CopyBytesToGo(args[4])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		sendReport, err := ch.api.PinMessage(adminPrivateKey, channelIdBytes,
+			targetMessageIdBytes, undoAction, cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(sendReport))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// 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 undoAction is true, then the targeted user will be unmuted.
+//
+// Parameters:
+//   - args[0] - The PEM-encoded admin RSA private key for the channel
+//     (Uint8Array).
+//   - args[1] - Marshalled bytes of channel [id.ID] (Uint8Array).
+//   - mutedUserPubKeyBytes - The [ed25519.PublicKey] of the user you want to
+//     mute.
+//   - args[3] - Set to true to un-delete the message (boolean).
+//   - args[4] - JSON of [xxdk.CMIXParams]. This may be empty, and
+//     [GetDefaultCMixParams] will be used internally (Uint8Array).
+//
+// Returns:
+//   - []byte - JSON of [ChannelSendReport].
+func (ch *ChannelsManager) MuteUser(_ js.Value, args []js.Value) interface{} {
+	adminPrivateKey := utils.CopyBytesToGo(args[0])
+	channelIdBytes := utils.CopyBytesToGo(args[1])
+	mutedUserPubKeyBytes := utils.CopyBytesToGo(args[2])
+	undoAction := args[3].Bool()
+	cmixParamsJSON := utils.CopyBytesToGo(args[4])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		sendReport, err := ch.api.MuteUser(adminPrivateKey, channelIdBytes,
+			mutedUserPubKeyBytes, undoAction, cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(sendReport))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
 // GetIdentity returns the marshaled public identity ([channel.Identity]) that
 // the channel is using.
 //
@@ -1224,6 +1344,27 @@ func IsNicknameValid(_ js.Value, args []js.Value) interface{} {
 	return nil
 }
 
+// Muted returns true if the user is currently muted in the given channel.
+//
+// Parameters:
+//   - args[0] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
+//
+// Returns:
+//   - Returns true if the user is muted in the channel and false otherwise
+//     (boolean).
+//   - Throws a TypeError if the channel ID cannot be unmarshalled.
+func (ch *ChannelsManager) Muted(_ js.Value, args []js.Value) interface{} {
+	channelIDBytes := utils.CopyBytesToGo(args[0])
+
+	muted, err := ch.api.Muted(channelIDBytes)
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return muted
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Channel Receiving Logic and Callback Registration                          //
 ////////////////////////////////////////////////////////////////////////////////
@@ -1345,11 +1486,11 @@ func (em *eventModel) LeaveChannel(channelID []byte) {
 //     later with [eventModel.UpdateSentStatus].
 func (em *eventModel) ReceiveMessage(channelID, messageID []byte, nickname,
 	text string, pubKey []byte, codeset int, timestamp, lease, roundId, msgType,
-	status int64) int64 {
+	status int64, hidden bool) int64 {
 	uuid := em.receiveMessage(utils.CopyBytesToJS(channelID),
 		utils.CopyBytesToJS(messageID), nickname, text,
 		utils.CopyBytesToJS(pubKey), codeset, timestamp, lease, roundId,
-		msgType, status)
+		msgType, status, hidden)
 
 	return int64(uuid.Int())
 }
@@ -1389,11 +1530,11 @@ func (em *eventModel) ReceiveMessage(channelID, messageID []byte, nickname,
 //     later with [eventModel.UpdateSentStatus].
 func (em *eventModel) ReceiveReply(channelID, messageID, reactionTo []byte,
 	senderUsername, text string, pubKey []byte, codeset int, timestamp, lease,
-	roundId, msgType, status int64) int64 {
+	roundId, msgType, status int64, hidden bool) int64 {
 	uuid := em.receiveReply(utils.CopyBytesToJS(channelID),
 		utils.CopyBytesToJS(messageID), utils.CopyBytesToJS(reactionTo),
 		senderUsername, text, utils.CopyBytesToJS(pubKey), codeset,
-		timestamp, lease, roundId, msgType, status)
+		timestamp, lease, roundId, msgType, status, hidden)
 
 	return int64(uuid.Int())
 }
@@ -1433,11 +1574,11 @@ func (em *eventModel) ReceiveReply(channelID, messageID, reactionTo []byte,
 //     later with [eventModel.UpdateSentStatus].
 func (em *eventModel) ReceiveReaction(channelID, messageID, reactionTo []byte,
 	senderUsername, reaction string, pubKey []byte, codeset int, timestamp,
-	lease, roundId, msgType, status int64) int64 {
+	lease, roundId, msgType, status int64, hidden bool) int64 {
 	uuid := em.receiveReaction(utils.CopyBytesToJS(channelID),
 		utils.CopyBytesToJS(messageID), utils.CopyBytesToJS(reactionTo),
 		senderUsername, reaction, utils.CopyBytesToJS(pubKey), codeset,
-		timestamp, lease, roundId, msgType, status)
+		timestamp, lease, roundId, msgType, status, hidden)
 
 	return int64(uuid.Int())
 }