diff --git a/lib/actions/holder-actions.js b/lib/actions/holder-actions.js
--- a/lib/actions/holder-actions.js
+++ b/lib/actions/holder-actions.js
@@ -1,7 +1,27 @@
 // @flow
 
+import type { BlobHashAndHolder } from '../types/holder-types.js';
+
+type MultipleBlobHolders = $ReadOnlyArray<BlobHashAndHolder>;
+
 export const storeEstablishedHolderActionType = 'STORE_ESTABLISHED_HOLDER';
-export type StoreEstablishedHolderPayload = {
-  +blobHash: string,
-  +holder: string,
+
+export const processHoldersActionTypes = Object.freeze({
+  started: 'PROCESS_HOLDERS_STARTED',
+  success: 'PROCESS_HOLDERS_SUCCESS',
+  failed: 'PROCESS_HOLDERS_FAILED',
+});
+export type ProcessHoldersStartedPayload = {
+  +holdersToAdd: MultipleBlobHolders,
+  +holdersToRemove: MultipleBlobHolders,
+};
+export type ProcessHoldersFailedPayload = {
+  +notAdded: MultipleBlobHolders,
+  +notRemoved: MultipleBlobHolders,
+};
+export type ProcessHoldersFinishedPayload = {
+  +added: MultipleBlobHolders,
+  +removed: MultipleBlobHolders,
+  +notAdded: MultipleBlobHolders,
+  +notRemoved: MultipleBlobHolders,
 };
diff --git a/lib/reducers/holder-reducer.js b/lib/reducers/holder-reducer.js
--- a/lib/reducers/holder-reducer.js
+++ b/lib/reducers/holder-reducer.js
@@ -1,9 +1,34 @@
 // @flow
 
-import { storeEstablishedHolderActionType } from '../actions/holder-actions.js';
-import { type HolderStore } from '../types/holder-types.js';
+import _omitBy from 'lodash/fp/omitBy.js';
+
+import {
+  processHoldersActionTypes,
+  storeEstablishedHolderActionType,
+} from '../actions/holder-actions.js';
+import {
+  type HolderInfo,
+  type HolderStatus,
+  type HolderStore,
+  type BlobHashAndHolder,
+} from '../types/holder-types.js';
 import type { BaseAction } from '../types/redux-types.js';
 
+const makeResultToStoreEntryMapper: (
+  status: HolderStatus,
+) => BlobHashAndHolder => [string, HolderInfo] =
+  status =>
+  ({ blobHash, holder }) => [blobHash, { holder, status }];
+
+const mapResultsToStoreEntries: (status: HolderStatus) => (
+  results: $ReadOnlyArray<BlobHashAndHolder>,
+) => {
+  +[blobHash: string]: HolderInfo,
+} = status => {
+  const mapFn = makeResultToStoreEntryMapper(status);
+  return results => Object.fromEntries(results.map(mapFn));
+};
+
 function reduceHolderStore(
   store: HolderStore,
   action: BaseAction,
@@ -21,6 +46,59 @@
         },
       },
     };
+  } else if (action.type === processHoldersActionTypes.started) {
+    // marks processed holders as pending
+    const { holdersToAdd, holdersToRemove } = action.payload;
+    const pendingEstablishmentHolders = mapResultsToStoreEntries(
+      'PENDING_ESTABLISHMENT',
+    )(holdersToAdd);
+    const pendingRemovalHolders =
+      mapResultsToStoreEntries('PENDING_REMOVAL')(holdersToRemove);
+    return {
+      ...store,
+      storedHolders: {
+        ...store.storedHolders,
+        ...pendingEstablishmentHolders,
+        ...pendingRemovalHolders,
+      },
+    };
+  } else if (action.type === processHoldersActionTypes.failed) {
+    const { notAdded, notRemoved } = action.payload;
+    const notEstablishedHolders =
+      mapResultsToStoreEntries('NOT_ESTABLISHED')(notAdded);
+    const notRemovedHolders =
+      mapResultsToStoreEntries('NOT_REMOVED')(notRemoved);
+    return {
+      ...store,
+      storedHolders: {
+        ...store.storedHolders,
+        ...notEstablishedHolders,
+        ...notRemovedHolders,
+      },
+    };
+  } else if (action.type === processHoldersActionTypes.success) {
+    const { added, removed, notAdded, notRemoved } = action.payload;
+
+    const removedBlobHashes = new Set(removed.map(({ blobHash }) => blobHash));
+    const filteredStore = _omitBy((_, blobHash) =>
+      removedBlobHashes.has(blobHash),
+    )(store.storedHolders);
+
+    const holdersAdded = mapResultsToStoreEntries('ESTABLISHED')(added);
+    const holdersNotAdded =
+      mapResultsToStoreEntries('NOT_ESTABLISHED')(notAdded);
+    const holdersNotRemoved =
+      mapResultsToStoreEntries('NOT_REMOVED')(notRemoved);
+
+    return {
+      ...store,
+      storedHolders: {
+        ...filteredStore,
+        ...holdersAdded,
+        ...holdersNotAdded,
+        ...holdersNotRemoved,
+      },
+    };
   }
   return store;
 }
diff --git a/lib/reducers/holder-reducer.test.js b/lib/reducers/holder-reducer.test.js
--- a/lib/reducers/holder-reducer.test.js
+++ b/lib/reducers/holder-reducer.test.js
@@ -1,10 +1,19 @@
 // @flow
 
 import { reduceHolderStore } from './holder-reducer.js';
-import { storeEstablishedHolderActionType } from '../actions/holder-actions.js';
+import {
+  processHoldersActionTypes,
+  storeEstablishedHolderActionType,
+} from '../actions/holder-actions.js';
 import { type HolderStore } from '../types/holder-types.js';
 import type { BaseAction } from '../types/redux-types.js';
 
+const mockLoadingInfo = {
+  fetchIndex: 0,
+  trackMultipleRequests: false,
+  customKeyName: null,
+};
+
 describe('reduceHolderStore', () => {
   it('STORE_ESTABLISHED_HOLDER', () => {
     const store: HolderStore = {
@@ -20,4 +29,83 @@
       foo: { holder: 'bar', status: 'ESTABLISHED' },
     });
   });
+
+  it('PROCESS_HOLDERS_STARTED', () => {
+    const store: HolderStore = {
+      storedHolders: {
+        blob1: { holder: 'holder1', status: 'ESTABLISHED' },
+      },
+    };
+    const action: BaseAction = {
+      type: processHoldersActionTypes.started,
+      payload: {
+        holdersToRemove: [{ blobHash: 'blob1', holder: 'holder1' }],
+        holdersToAdd: [{ blobHash: 'blob2', holder: 'holder2' }],
+      },
+      loadingInfo: mockLoadingInfo,
+    };
+
+    expect(reduceHolderStore(store, action).storedHolders).toStrictEqual({
+      blob1: { holder: 'holder1', status: 'PENDING_REMOVAL' },
+      blob2: { holder: 'holder2', status: 'PENDING_ESTABLISHMENT' },
+    });
+  });
+
+  it('PROCESS_HOLDERS_FAILED', () => {
+    const store: HolderStore = {
+      storedHolders: {
+        blob1: { holder: 'holder1', status: 'ESTABLISHED' },
+        blob2: { holder: 'holder2', status: 'PENDING_ESTABLISHMENT' },
+        blob3: { holder: 'holder3', status: 'PENDING_REMOVAL' },
+      },
+    };
+
+    const errorPayload: any = new Error('mock error');
+    errorPayload.notAdded = [{ blobHash: 'blob2', holder: 'holder2' }];
+    errorPayload.notRemoved = [{ blobHash: 'blob3', holder: 'holder3' }];
+
+    const action: BaseAction = {
+      type: processHoldersActionTypes.failed,
+      error: true,
+      payload: errorPayload,
+      loadingInfo: mockLoadingInfo,
+    };
+
+    expect(reduceHolderStore(store, action).storedHolders).toStrictEqual({
+      blob1: { holder: 'holder1', status: 'ESTABLISHED' },
+      blob2: { holder: 'holder2', status: 'NOT_ESTABLISHED' },
+      blob3: { holder: 'holder3', status: 'NOT_REMOVED' },
+    });
+  });
+
+  it('PROCESS_HOLDERS_SUCCESS', () => {
+    const store: HolderStore = {
+      storedHolders: {
+        blob1: { holder: 'holder1', status: 'ESTABLISHED' },
+        blob2: { holder: 'holder2', status: 'PENDING_ESTABLISHMENT' },
+        blob3: { holder: 'holder3', status: 'PENDING_ESTABLISHMENT' },
+        blob4: { holder: 'holder4', status: 'PENDING_REMOVAL' },
+        blob5: { holder: 'holder5', status: 'PENDING_REMOVAL' },
+      },
+    };
+
+    const action: BaseAction = {
+      type: processHoldersActionTypes.success,
+      payload: {
+        added: [{ blobHash: 'blob2', holder: 'holder2' }],
+        notAdded: [{ blobHash: 'blob3', holder: 'holder3' }],
+        removed: [{ blobHash: 'blob4', holder: 'holder4' }],
+        notRemoved: [{ blobHash: 'blob5', holder: 'holder5' }],
+      },
+      loadingInfo: mockLoadingInfo,
+    };
+
+    expect(reduceHolderStore(store, action).storedHolders).toStrictEqual({
+      blob1: { holder: 'holder1', status: 'ESTABLISHED' },
+      blob2: { holder: 'holder2', status: 'ESTABLISHED' },
+      blob3: { holder: 'holder3', status: 'NOT_ESTABLISHED' },
+      // blob4 removed
+      blob5: { holder: 'holder5', status: 'NOT_REMOVED' },
+    });
+  });
 });
diff --git a/lib/types/holder-types.js b/lib/types/holder-types.js
--- a/lib/types/holder-types.js
+++ b/lib/types/holder-types.js
@@ -22,3 +22,8 @@
   +type: 'establish_holder' | 'remove_holder',
   +blobHash: string,
 };
+
+export type BlobHashAndHolder = {
+  +blobHash: string,
+  +holder: string,
+};
diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js
--- a/lib/types/redux-types.js
+++ b/lib/types/redux-types.js
@@ -70,7 +70,7 @@
   CalendarThreadFilter,
   SetCalendarDeletedFilterPayload,
 } from './filter-types.js';
-import type { HolderStore } from './holder-types.js';
+import type { HolderStore, BlobHashAndHolder } from './holder-types.js';
 import type { IdentityAuthResult } from './identity-service-types';
 import type { IntegrityStore } from './integrity-types.js';
 import type {
@@ -164,7 +164,11 @@
   SetDeviceTokenActionPayload,
   SetDeviceTokenStartedPayload,
 } from '../actions/device-actions.js';
-import type { StoreEstablishedHolderPayload } from '../actions/holder-actions.js';
+import type {
+  ProcessHoldersStartedPayload,
+  ProcessHoldersFailedPayload,
+  ProcessHoldersFinishedPayload,
+} from '../actions/holder-actions.js';
 import type {
   UpdateConnectionStatusPayload,
   SetLateResponsePayload,
@@ -1612,7 +1616,23 @@
       }
     | {
         +type: 'STORE_ESTABLISHED_HOLDER',
-        +payload: StoreEstablishedHolderPayload,
+        +payload: BlobHashAndHolder,
+      }
+    | {
+        +type: 'PROCESS_HOLDERS_STARTED',
+        +payload: ProcessHoldersStartedPayload,
+        +loadingInfo: LoadingInfo,
+      }
+    | {
+        +type: 'PROCESS_HOLDERS_FAILED',
+        +error: true,
+        +payload: Error & ProcessHoldersFailedPayload,
+        +loadingInfo: LoadingInfo,
+      }
+    | {
+        +type: 'PROCESS_HOLDERS_SUCCESS',
+        +payload: ProcessHoldersFinishedPayload,
+        +loadingInfo: LoadingInfo,
       },
 }>;