diff --git a/lib/actions/thread-actions.js b/lib/actions/thread-actions.js
--- a/lib/actions/thread-actions.js
+++ b/lib/actions/thread-actions.js
@@ -1,12 +1,28 @@
 // @flow
 
 import invariant from 'invariant';
+import * as React from 'react';
+import uuid from 'uuid';
 
 import genesis from '../facts/genesis.js';
 import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
 import { useKeyserverCall } from '../keyserver-conn/keyserver-call.js';
 import type { CallKeyserverEndpoint } from '../keyserver-conn/keyserver-conn-types.js';
+import {
+  type OutboundDMOperationSpecification,
+  dmOperationSpecificationTypes,
+} from '../shared/dm-ops/dm-op-utils.js';
+import { useProcessAndSendDMOperation } from '../shared/dm-ops/process-dm-ops.js';
 import { permissionsAndAuthRelatedRequestTimeout } from '../shared/timeouts.js';
+import type {
+  DMChangeThreadSettingsOperation,
+  DMThreadSettingsChangesBase,
+} from '../types/dm-ops.js';
+import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js';
+import {
+  thickThreadTypes,
+  threadTypeIsThick,
+} from '../types/thread-types-enum.js';
 import type {
   ChangeThreadSettingsPayload,
   LeaveThreadPayload,
@@ -23,6 +39,7 @@
   RoleDeletionPayload,
 } from '../types/thread-types.js';
 import { values } from '../utils/objects.js';
+import { useSelector } from '../utils/redux-utils.js';
 
 export type DeleteThreadInput = {
   +threadID: string,
@@ -97,10 +114,77 @@
     };
   };
 
-function useChangeThreadSettings(): (
-  input: UpdateThreadRequest,
-) => Promise<ChangeThreadSettingsPayload> {
-  return useKeyserverCall(changeThreadSettings);
+function useChangeThreadSettings(
+  threadInfo: ?ThreadInfo,
+): (input: UpdateThreadRequest) => Promise<ChangeThreadSettingsPayload> {
+  const processAndSendDMOperation = useProcessAndSendDMOperation();
+  const viewerID = useSelector(
+    state => state.currentUserInfo && state.currentUserInfo.id,
+  );
+
+  const keyserverCall = useKeyserverCall(changeThreadSettings);
+
+  return React.useCallback(
+    async (input: UpdateThreadRequest) => {
+      if (!threadInfo || !threadTypeIsThick(threadInfo.type)) {
+        return await keyserverCall(input);
+      }
+
+      invariant(viewerID, 'viewerID should be set');
+
+      const changes: { ...DMThreadSettingsChangesBase } = {};
+      if (input.changes.name) {
+        changes.name = input.changes.name;
+      }
+      if (input.changes.description) {
+        changes.description = input.changes.description;
+      }
+      if (input.changes.color) {
+        changes.color = input.changes.color;
+      }
+      if (input.changes.avatar && input.changes.avatar.type === 'emoji') {
+        changes.avatar = {
+          type: 'emoji',
+          emoji: input.changes.avatar.emoji,
+          color: input.changes.avatar.color,
+        };
+      } else if (input.changes.avatar && input.changes.avatar.type === 'ens') {
+        changes.avatar = { type: 'ens' };
+      }
+      // To support `image` and `encrypted_image` avatars we first, need stop
+      // sending multimedia metadata to keyserver.
+      // ENG-8708
+
+      const op: DMChangeThreadSettingsOperation = {
+        type: 'change_thread_settings',
+        threadID: threadInfo.id,
+        editorID: viewerID,
+        time: Date.now(),
+        changes,
+        messageIDsPrefix: uuid.v4(),
+      };
+      const opSpecification: OutboundDMOperationSpecification = {
+        type: dmOperationSpecificationTypes.OUTBOUND,
+        op,
+        recipients: {
+          type: 'all_thread_members',
+          threadID:
+            threadInfo.type === thickThreadTypes.THICK_SIDEBAR &&
+            threadInfo.parentThreadID
+              ? threadInfo.parentThreadID
+              : threadInfo.id,
+        },
+      };
+
+      await processAndSendDMOperation(opSpecification);
+      return ({
+        threadID: threadInfo.id,
+        updatesResult: { newUpdates: [] },
+        newMessageInfos: [],
+      }: ChangeThreadSettingsPayload);
+    },
+    [keyserverCall, processAndSendDMOperation, threadInfo, viewerID],
+  );
 }
 
 export type RemoveUsersFromThreadInput = {
diff --git a/lib/components/base-edit-thread-avatar-provider.react.js b/lib/components/base-edit-thread-avatar-provider.react.js
--- a/lib/components/base-edit-thread-avatar-provider.react.js
+++ b/lib/components/base-edit-thread-avatar-provider.react.js
@@ -7,6 +7,7 @@
   changeThreadSettingsActionTypes,
 } from '../actions/thread-actions.js';
 import { createLoadingStatusSelector } from '../selectors/loading-selectors.js';
+import { threadInfoSelector } from '../selectors/thread-selectors.js';
 import type { UpdateUserAvatarRequest } from '../types/avatar-types.js';
 import type { LoadingStatus } from '../types/loading-types.js';
 import type { UpdateThreadRequest } from '../types/thread-types.js';
@@ -39,8 +40,11 @@
     ),
   );
 
+  const threadInfo = useSelector(
+    state => threadInfoSelector(state)[activeThreadID],
+  );
   const dispatchActionPromise = useDispatchActionPromise();
-  const changeThreadSettingsCall = useChangeThreadSettings();
+  const changeThreadSettingsCall = useChangeThreadSettings(threadInfo);
 
   const [
     threadAvatarMediaUploadInProgress,
diff --git a/native/chat/settings/color-selector-modal.react.js b/native/chat/settings/color-selector-modal.react.js
--- a/native/chat/settings/color-selector-modal.react.js
+++ b/native/chat/settings/color-selector-modal.react.js
@@ -176,7 +176,9 @@
     const windowWidth = useSelector(state => state.dimensions.width);
 
     const dispatchActionPromise = useDispatchActionPromise();
-    const callChangeThreadSettings = useChangeThreadSettings();
+    const callChangeThreadSettings = useChangeThreadSettings(
+      props.route.params.threadInfo,
+    );
 
     return (
       <ColorSelectorModal
diff --git a/native/chat/settings/thread-settings-description.react.js b/native/chat/settings/thread-settings-description.react.js
--- a/native/chat/settings/thread-settings-description.react.js
+++ b/native/chat/settings/thread-settings-description.react.js
@@ -306,7 +306,7 @@
     const styles = useStyles(unboundStyles);
 
     const dispatchActionPromise = useDispatchActionPromise();
-    const callChangeThreadSettings = useChangeThreadSettings();
+    const callChangeThreadSettings = useChangeThreadSettings(props.threadInfo);
 
     const canEditThreadDescription = useThreadHasPermission(
       props.threadInfo,
diff --git a/native/chat/settings/thread-settings-name.react.js b/native/chat/settings/thread-settings-name.react.js
--- a/native/chat/settings/thread-settings-name.react.js
+++ b/native/chat/settings/thread-settings-name.react.js
@@ -230,7 +230,7 @@
     );
 
     const dispatchActionPromise = useDispatchActionPromise();
-    const callChangeThreadSettings = useChangeThreadSettings();
+    const callChangeThreadSettings = useChangeThreadSettings(props.threadInfo);
 
     return (
       <ThreadSettingsName
diff --git a/web/modals/threads/settings/thread-settings-utils.js b/web/modals/threads/settings/thread-settings-utils.js
--- a/web/modals/threads/settings/thread-settings-utils.js
+++ b/web/modals/threads/settings/thread-settings-utils.js
@@ -32,7 +32,7 @@
     params;
 
   const dispatchActionPromise = useDispatchActionPromise();
-  const callChangeThreadSettings = useChangeThreadSettings();
+  const callChangeThreadSettings = useChangeThreadSettings(threadInfo);
 
   const changeThreadSettingsAction = React.useCallback(async () => {
     try {