diff --git a/Makefile b/Makefile
index 693d4c626a8ceecb85204819f36ccca46e203d3c..1567103e23775c56f6d6139b777d2e2274638ab8 100644
--- a/Makefile
+++ b/Makefile
@@ -11,20 +11,20 @@ build:
 	GOOS=js GOARCH=wasm go build ./...
 
 update_release:
+	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@release
 	GOFLAGS="" go get gitlab.com/xx_network/primitives@release
 	GOFLAGS="" go get gitlab.com/elixxir/primitives@release
 	GOFLAGS="" go get gitlab.com/xx_network/crypto@release
 	GOFLAGS="" go get gitlab.com/elixxir/crypto@release
 	GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@release
-	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@release
 
 update_master:
+	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@master
 	GOFLAGS="" go get gitlab.com/xx_network/primitives@master
 	GOFLAGS="" go get gitlab.com/elixxir/primitives@master
 	GOFLAGS="" go get gitlab.com/xx_network/crypto@master
 	GOFLAGS="" go get gitlab.com/elixxir/crypto@master
 	GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@master
-	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@master
 
 binary:
 	GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o xxdk.wasm main.go
diff --git a/indexedDb/impl/dm/implementation.go b/indexedDb/impl/dm/implementation.go
index f40791ece46d05b397dadd6766ec455045168452..09303bcfa7747ef203d262c4da8e36b62c3daabe 100644
--- a/indexedDb/impl/dm/implementation.go
+++ b/indexedDb/impl/dm/implementation.go
@@ -179,13 +179,7 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64, messageID message.ID,
 	}
 
 	// Extract the existing Message and update the Status
-	newMessage := &Message{}
-	err = json.Unmarshal([]byte(utils.JsToJson(currentMsg)), newMessage)
-	if err != nil {
-		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
-			"Could not JSON unmarshal message: %+v", err))
-		return
-	}
+	newMessage, err := valueToMessage(currentMsg)
 
 	newMessage.Status = uint8(status)
 	if !messageID.Equals(message.ID{}) {
@@ -365,6 +359,45 @@ func (w *wasmModel) setBlocked(senderPubKey ed25519.PublicKey, isBlocked bool) e
 		resultConvo.Token, resultConvo.CodesetVersion, timeBlocked)
 }
 
+// DeleteMessage deletes the message with the given message.ID belonging to
+// the sender. If the message exists and belongs to the sender, then it is
+// deleted and DeleteMessage returns true. If it does not exist, it returns
+// false.
+func (w *wasmModel) DeleteMessage(messageID message.ID, senderPubKey ed25519.PublicKey) bool {
+	parentErr := "failed to DeleteMessage"
+	msgId := impl.EncodeBytes(messageID.Marshal())
+
+	// Use the key to get the existing Message
+	currentMsg, err := impl.GetIndex(w.db, messageStoreName,
+		messageStoreMessageIndex, msgId)
+	if err != nil {
+		jww.ERROR.Printf("%s: %+v", parentErr, err)
+		return false
+	}
+
+	// Convert the js.Value to a proper object
+	msgObj, err := valueToMessage(currentMsg)
+	if err != nil {
+		jww.ERROR.Printf("%s: %+v", parentErr, err)
+		return false
+	}
+
+	// Ensure the public keys match
+	if !bytes.Equal(msgObj.SenderPubKey, senderPubKey) {
+		jww.ERROR.Printf("%s: %s", parentErr, "Public keys do not match")
+		return false
+	}
+
+	// Perform the delete
+	err = impl.DeleteIndex(w.db, messageStoreName, messageStoreMessageIndex,
+		msgPkeyName, msgId)
+	if err != nil {
+		jww.ERROR.Printf("%s: %+v", parentErr, err)
+		return false
+	}
+	return true
+}
+
 // GetConversation returns the conversation held by the model (receiver).
 func (w *wasmModel) GetConversation(senderPubKey ed25519.PublicKey) *dm.ModelConversation {
 	parentErr := "failed to GetConversation"
@@ -426,3 +459,9 @@ func (w *wasmModel) GetConversations() []dm.ModelConversation {
 	}
 	return conversations
 }
+
+// valueToMessage is a helper for converting js.Value to Message.
+func valueToMessage(msgObj js.Value) (*Message, error) {
+	resultMsg := &Message{}
+	return resultMsg, json.Unmarshal([]byte(utils.JsToJson(msgObj)), resultMsg)
+}
diff --git a/indexedDb/impl/dm/implementation_test.go b/indexedDb/impl/dm/implementation_test.go
index c8f42c3871bcfc841683e93b57e2ed4105ab000b..39ca0036a078382ca232a2a725ff7a5ff412dddf 100644
--- a/indexedDb/impl/dm/implementation_test.go
+++ b/indexedDb/impl/dm/implementation_test.go
@@ -14,6 +14,7 @@ import (
 	"crypto/ed25519"
 	"encoding/json"
 	"fmt"
+	"github.com/stretchr/testify/require"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	"gitlab.com/elixxir/client/v4/dm"
 	"gitlab.com/elixxir/crypto/message"
@@ -158,3 +159,36 @@ func TestWasmModel_BlockSender(t *testing.T) {
 		t.Fatal("Expected blocked to be false")
 	}
 }
+
+// Test failed and successful deletes
+func TestWasmModel_DeleteMessage(t *testing.T) {
+	m, err := newWASMModel("TestWasmModel_DeleteMessage", nil, dummyReceivedMessageCB)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+
+	// Insert test message
+	testBytes := []byte("test")
+	testBadBytes := []byte("uwu")
+	testMsgId := message.DeriveChannelMessageID(&id.ID{1}, 0, testBytes)
+	testMsg := &Message{
+		MessageID:          testMsgId.Marshal(),
+		ConversationPubKey: testBytes,
+		ParentMessageID:    nil,
+		Timestamp:          time.Now(),
+		SenderPubKey:       testBytes,
+		CodesetVersion:     5,
+		Status:             5,
+		Text:               "",
+		Type:               5,
+		Round:              5,
+	}
+	_, err = m.upsertMessage(testMsg)
+	require.NoError(t, err)
+
+	// Non-matching pub key, should fail to delete
+	require.False(t, m.DeleteMessage(testMsgId, testBadBytes))
+
+	// Correct pub key, should have deleted
+	require.True(t, m.DeleteMessage(testMsgId, testBytes))
+}
diff --git a/wasm/channels.go b/wasm/channels.go
index b9b7e76de892c1ccc4b4e7dbda312a343101f7a5..058cfa38806cc44c3fa5128d96c074aad37bdac2 100644
--- a/wasm/channels.go
+++ b/wasm/channels.go
@@ -1022,16 +1022,22 @@ func ValidForever(js.Value, []js.Value) any {
 //     to the user should be tracked while all actions should not be (boolean).
 //   - args[5] - JSON of [xxdk.CMIXParams]. If left empty
 //     [bindings.GetDefaultCMixParams] will be used internally (Uint8Array).
-//   - args[6] - JSON of a slice of public keys of users that should receive
-//     mobile notifications for the message.
+//   - args[6] - JSON of a map of slices of [ed25519.PublicKey] of users that
+//     should receive mobile notifications for the message. Each slice keys on a
+//     [channels.PingType] that describes the type of notification it is
+//     (Uint8Array).
 //
-// Example slice of public keys:
+// Example map of slices of public keys:
 //
-//	[
-//	  "FgJMvgSsY4rrKkS/jSe+vFOJOs5qSSyOUSW7UtF9/KU=",
-//	  "fPqcHtrJ398PAC35QyWXEU9PHzz8Z4BKQTCxSvpSygw=",
-//	  "JnjCgh7g/+hNiI9VPKW01aRSxGOFmNulNCymy3ImXAo="
-//	]
+//	{
+//	  "usrMention": [
+//	    "CLdKxbe8D2WVOpx1mT63TZ5CP/nesmxHLT5DUUalpe0=",
+//	    "S2c6NXjNqgR11SCOaiQUughWaLpWBKNufPt6cbTVHMA="
+//	  ],
+//	  "usrReply": [
+//	    "aaMzSeA6Cu2Aix2MlOwzrAI+NnpKshzvZRT02PZPVec="
+//	  ]
+//	}
 //
 // Returns a promise:
 //   - Resolves to the JSON of [bindings.ChannelSendReport] (Uint8Array).
@@ -1043,11 +1049,11 @@ func (cm *ChannelsManager) SendGeneric(_ js.Value, args []js.Value) any {
 	leaseTimeMS := int64(args[3].Int())
 	tracked := args[4].Bool()
 	cmixParamsJSON := utils.CopyBytesToGo(args[5])
-	pingsJSON := utils.CopyBytesToGo(args[6])
+	pingsMapJSON := utils.CopyBytesToGo(args[6])
 
 	promiseFn := func(resolve, reject func(args ...any) js.Value) {
 		sendReport, err := cm.api.SendGeneric(marshalledChanId, messageType,
-			msg, leaseTimeMS, tracked, cmixParamsJSON, pingsJSON)
+			msg, leaseTimeMS, tracked, cmixParamsJSON, pingsMapJSON)
 		if err != nil {
 			reject(exception.NewTrace(err))
 		} else {
@@ -1782,8 +1788,21 @@ func (cm *ChannelsManager) SetMobileNotificationsLevel(_ js.Value,
 // Example return:
 //
 //	[
-//	  {"channel":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD","type":1},
-//	  {"channel":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD","type":2}
+//	  {
+//	    "channel": "jOgZopfYj4zrE/AHtKmkf+QEWnfUKv9KfIy/+Bsg0PkD",
+//	    "type": 1,
+//	    "pingType": "usrMention"
+//	  },
+//	  {
+//	    "channel": "GKmfN/LKXQYM6++TC6DeZYqoxvSUPkh5UAHWODqh9zkD",
+//	    "type": 2,
+//	    "pingType": "usrReply"
+//	  },
+//	  {
+//	    "channel": "M+28xtj0coHrhDHfojGNcyb2c4maO7ZuheB6egS0Pc4D",
+//	    "type": 1,
+//	    "pingType": ""
+//	  }
 //	]
 func GetChannelNotificationReportsForMe(_ js.Value, args []js.Value) any {
 	notificationFilterJSON := utils.CopyBytesToGo(args[0])