diff --git a/lib/shared/dm-ops/dm-op-utils.js b/lib/shared/dm-ops/dm-op-utils.js
--- a/lib/shared/dm-ops/dm-op-utils.js
+++ b/lib/shared/dm-ops/dm-op-utils.js
@@ -9,6 +9,7 @@
   outboundP2PMessageStatuses,
   type OutboundP2PMessage,
 } from '../../types/sqlite-types.js';
+import type { RawThreadInfos } from '../../types/thread-types.js';
 import {
   type DMOperationP2PMessage,
   userActionsP2PMessageTypes,
@@ -57,7 +58,8 @@
   +op: DMOperation,
   +recipients:
     | { +type: 'all_peer_devices' | 'self_devices' }
-    | { +type: 'some_users', +userIDs: $ReadOnlyArray<string> },
+    | { +type: 'some_users', +userIDs: $ReadOnlyArray<string> }
+    | { +type: 'all_thread_members', +threadID: string },
   +sendOnly?: boolean,
 };
 
@@ -80,6 +82,7 @@
     +deviceID: string,
   }>,
   currentUserInfo: ?CurrentUserInfo,
+  threadInfos: RawThreadInfos,
 ): Promise<$ReadOnlyArray<OutboundP2PMessage>> {
   if (!currentUserInfo?.id) {
     return [];
@@ -95,6 +98,14 @@
     peerUserIDAndDeviceIDs = allPeerUserIDAndDeviceIDs.filter(peer =>
       userIDs.has(peer.userID),
     );
+  } else if (operation.recipients.type === 'all_thread_members') {
+    const members = threadInfos[operation.recipients.threadID]?.members ?? [];
+    const memberIDs = members.map(member => member.id);
+
+    const userIDs = new Set(memberIDs);
+    peerUserIDAndDeviceIDs = allPeerUserIDAndDeviceIDs.filter(peer =>
+      userIDs.has(peer.userID),
+    );
   }
 
   const thisDeviceID = await getContentSigningKey();
diff --git a/lib/shared/dm-ops/process-dm-ops.js b/lib/shared/dm-ops/process-dm-ops.js
--- a/lib/shared/dm-ops/process-dm-ops.js
+++ b/lib/shared/dm-ops/process-dm-ops.js
@@ -78,6 +78,7 @@
           dmOperationSpecification,
           allPeerUserIDAndDeviceIDs,
           currentUserInfo,
+          threadInfos,
         );
       }