diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js
--- a/keyserver/src/endpoints.js
+++ b/keyserver/src/endpoints.js
@@ -26,6 +26,7 @@
   multimediaMessageCreationResponder,
   reactionMessageCreationResponder,
   editMessageCreationResponder,
+  fetchPinnedMessagesResponder,
 } from './responders/message-responders.js';
 import { updateRelationshipsResponder } from './responders/relationship-responders.js';
 import {
@@ -141,6 +142,10 @@
     responder: messageFetchResponder,
     requiredPolicies: baseLegalPolicies,
   },
+  fetch_pinned_messages: {
+    responder: fetchPinnedMessagesResponder,
+    requiredPolicies: baseLegalPolicies,
+  },
   fetch_thread_media: {
     responder: threadFetchMediaResponder,
     requiredPolicies: baseLegalPolicies,
diff --git a/keyserver/src/responders/message-responders.js b/keyserver/src/responders/message-responders.js
--- a/keyserver/src/responders/message-responders.js
+++ b/keyserver/src/responders/message-responders.js
@@ -21,6 +21,8 @@
   defaultNumberPerThread,
   type SendMessageResponse,
   type SendEditMessageResponse,
+  type FetchPinnedMessagesRequest,
+  type FetchPinnedMessagesResult,
 } from 'lib/types/message-types.js';
 import type { EditMessageData } from 'lib/types/messages/edit.js';
 import type { ReactionMessageData } from 'lib/types/messages/reaction.js';
@@ -41,6 +43,7 @@
   fetchMessageInfoForLocalID,
   fetchMessageInfoByID,
   fetchThreadMessagesCount,
+  fetchPinnedMessageInfos,
 } from '../fetchers/message-fetchers.js';
 import { fetchServerThreadInfos } from '../fetchers/thread-fetchers.js';
 import { checkThreadPermission } from '../fetchers/thread-permission-fetchers.js';
@@ -382,10 +385,27 @@
   return { newMessageInfos };
 }
 
+const fetchPinnedMessagesResponderInputValidator = tShape({
+  threadID: t.String,
+});
+async function fetchPinnedMessagesResponder(
+  viewer: Viewer,
+  input: any,
+): Promise<FetchPinnedMessagesResult> {
+  const request: FetchPinnedMessagesRequest = input;
+  await validateInput(
+    viewer,
+    fetchPinnedMessagesResponderInputValidator,
+    input,
+  );
+  return await fetchPinnedMessageInfos(viewer, request);
+}
+
 export {
   textMessageCreationResponder,
   messageFetchResponder,
   multimediaMessageCreationResponder,
   reactionMessageCreationResponder,
   editMessageCreationResponder,
+  fetchPinnedMessagesResponder,
 };
diff --git a/lib/actions/message-actions.js b/lib/actions/message-actions.js
--- a/lib/actions/message-actions.js
+++ b/lib/actions/message-actions.js
@@ -9,6 +9,8 @@
   SendReactionMessageRequest,
   SimpleMessagesPayload,
   SendEditMessageRequest,
+  FetchPinnedMessagesRequest,
+  FetchPinnedMessagesResult,
 } from '../types/message-types.js';
 import type { MediaMessageServerDBContent } from '../types/messages/media.js';
 import type {
@@ -264,6 +266,17 @@
 const processMessagesActionType = 'PROCESS_MESSAGES';
 const messageStorePruneActionType = 'MESSAGE_STORE_PRUNE';
 
+const fetchPinnedMessages =
+  (
+    callServerEndpoint: CallServerEndpoint,
+  ): ((
+    request: FetchPinnedMessagesRequest,
+  ) => Promise<FetchPinnedMessagesResult>) =>
+  async request => {
+    const response = await callServerEndpoint('fetch_pinned_messages', request);
+    return { pinnedMessages: response.pinnedMessages };
+  };
+
 export {
   fetchMessagesBeforeCursorActionTypes,
   fetchMessagesBeforeCursor,
@@ -284,4 +297,5 @@
   messageStorePruneActionType,
   sendEditMessageActionTypes,
   sendEditMessage,
+  fetchPinnedMessages,
 };
diff --git a/lib/types/endpoints.js b/lib/types/endpoints.js
--- a/lib/types/endpoints.js
+++ b/lib/types/endpoints.js
@@ -62,6 +62,7 @@
   FETCH_ENTRY_REVISIONS: 'fetch_entry_revisions',
   FETCH_ERROR_REPORT_INFOS: 'fetch_error_report_infos',
   FETCH_MESSAGES: 'fetch_messages',
+  FETCH_PINNED_MESSAGES: 'fetch_pinned_messages',
   FETCH_THREAD_MEDIA: 'fetch_thread_media',
   GET_SESSION_PUBLIC_KEYS: 'get_session_public_keys',
   JOIN_THREAD: 'join_thread',