diff --git a/apps/meteor/app/e2e/client/rocketchat.e2e.ts b/apps/meteor/app/e2e/client/rocketchat.e2e.ts
index c770c4a23ebc727d0bc16ad0ac219700c9c49127..cdb088de99d12f82d8e0dcfc3c64f77e22862fe1 100644
--- a/apps/meteor/app/e2e/client/rocketchat.e2e.ts
+++ b/apps/meteor/app/e2e/client/rocketchat.e2e.ts
@@ -44,6 +44,11 @@ import { mapMessageFromApi } from '../../../client/lib/utils/mapMessageFromApi';
 
 let failedToDecodeKey = false;
 
+type KeyPair = {
+	public_key: string | null;
+	private_key: string | null;
+};
+
 class E2E extends Emitter {
 	private started: boolean;
 
@@ -114,19 +119,28 @@ class E2E extends Emitter {
 		delete this.instancesByRoomId[rid];
 	}
 
-	async persistKeys(public_key: string | null, private_key: string | null): Promise<void> {
+	async persistKeys({ public_key, private_key }: KeyPair, password: string): Promise<void> {
 		if (typeof public_key !== 'string' || typeof private_key !== 'string') {
 			throw new Error('Failed to persist keys as they are not strings.');
 		}
 
+		const encodedPrivateKey = await this.encodePrivateKey(private_key, password);
+
+		if (!encodedPrivateKey) {
+			throw new Error('Failed to encode private key with provided password.');
+		}
+
 		await APIClient.post('/v1/e2e.setUserPublicAndPrivateKeys', {
 			public_key,
-			private_key,
+			private_key: encodedPrivateKey,
 		});
 	}
 
-	getKeysFromLocalStorage(): [public_key: string | null, private_key: string | null] {
-		return [Meteor._localStorage.getItem('public_key'), Meteor._localStorage.getItem('private_key')];
+	getKeysFromLocalStorage(): KeyPair {
+		return {
+			public_key: Meteor._localStorage.getItem('public_key'),
+			private_key: Meteor._localStorage.getItem('private_key'),
+		};
 	}
 
 	async startClient(): Promise<void> {
@@ -138,9 +152,7 @@ class E2E extends Emitter {
 
 		this.started = true;
 
-		const [localPublicKey, localPrivateKey] = this.getKeysFromLocalStorage();
-		let public_key = localPublicKey;
-		let private_key = localPrivateKey;
+		let { public_key, private_key } = this.getKeysFromLocalStorage();
 
 		await this.loadKeysFromDB();
 
@@ -176,7 +188,7 @@ class E2E extends Emitter {
 		}
 
 		if (!this.db_public_key || !this.db_private_key) {
-			this.persistKeys(...this.getKeysFromLocalStorage());
+			this.persistKeys(this.getKeysFromLocalStorage(), await this.createRandomPassword());
 		}
 
 		const randomPassword = Meteor._localStorage.getItem('e2e.randomPassword');
@@ -229,7 +241,7 @@ class E2E extends Emitter {
 	}
 
 	async changePassword(newPassword: string): Promise<void> {
-		await this.persistKeys(...this.getKeysFromLocalStorage());
+		await this.persistKeys(this.getKeysFromLocalStorage(), newPassword);
 
 		if (Meteor._localStorage.getItem('e2e.randomPassword')) {
 			Meteor._localStorage.setItem('e2e.randomPassword', newPassword);
diff --git a/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.js b/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.js
index d6c3f35dd963801f4c283c6f4f901dacf3934b91..cbc9b0fc5b3ec500a7efcc63152cc3cf8980ed82 100644
--- a/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.js
+++ b/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.js
@@ -230,6 +230,7 @@ export class CachedCollection extends Emitter {
 		data.forEach((record) => {
 			const newRecord = callbacks.run(`cachedCollection-loadFromServer-${this.name}`, record, 'changed');
 			this.collection.direct.upsert({ _id: newRecord._id }, omit(newRecord, '_id'));
+			callbacks.run(`cachedCollection-after-loadFromServer-${this.name}`, record, 'changed');
 
 			this.onSyncData('changed', newRecord);
 
@@ -296,6 +297,7 @@ export class CachedCollection extends Emitter {
 				const { _id, ...recordData } = newRecord;
 				this.collection.direct.upsert({ _id }, recordData);
 			}
+			callbacks.run(`cachedCollection-after-received-${this.name}`, record, t);
 			this.save();
 		});
 	}
@@ -362,6 +364,7 @@ export class CachedCollection extends Emitter {
 			if (actionTime > this.updatedAt) {
 				this.updatedAt = actionTime;
 			}
+			callbacks.run(`cachedCollection-after-sync-${this.name}`, record, action);
 			this.onSyncData(action, newRecord);
 		}
 		this.updatedAt = this.updatedAt === lastTime ? startTime : this.updatedAt;
diff --git a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx
index 32ed9924a3d7d20cbef5559ba06f3c74404bc67b..1b0f25716b51dc433d2f0a16d7906c6b281435a9 100644
--- a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx
+++ b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx
@@ -259,7 +259,13 @@ const CreateChannelModal = ({ teamId = '', onClose }: CreateChannelModalProps):
 								control={control}
 								name='encrypted'
 								render={({ field: { onChange, value, ref } }): ReactElement => (
-									<ToggleSwitch ref={ref} checked={value} disabled={e2eDisabled || federated} onChange={onChange} />
+									<ToggleSwitch
+										ref={ref}
+										checked={value}
+										disabled={e2eDisabled || federated}
+										onChange={onChange}
+										data-qa-type='encryption-toggle'
+									/>
 								)}
 							/>
 						</Box>
diff --git a/apps/meteor/client/views/account/security/AccountSecurityPage.tsx b/apps/meteor/client/views/account/security/AccountSecurityPage.tsx
index e6017b943d7ec97ebd9299c617f5b8f6745b833c..0c4c77112e68b6b749cf8da5e0131821f374e757 100644
--- a/apps/meteor/client/views/account/security/AccountSecurityPage.tsx
+++ b/apps/meteor/client/views/account/security/AccountSecurityPage.tsx
@@ -34,7 +34,7 @@ const AccountSecurityPage = (): ReactElement => {
 							</Accordion.Item>
 						)}
 						{e2eEnabled && (
-							<Accordion.Item title={t('E2E Encryption')} defaultExpanded={!twoFactorEnabled}>
+							<Accordion.Item title={t('E2E Encryption')} defaultExpanded={!twoFactorEnabled} data-qa-type='e2e-encryption-section'>
 								<EndToEnd />
 							</Accordion.Item>
 						)}
diff --git a/apps/meteor/client/views/account/security/EndToEnd.tsx b/apps/meteor/client/views/account/security/EndToEnd.tsx
index 283dd1bd757e39922e5364fc0a490080126c07b6..56df472c8f4fb963c1314fab2fc610ba9a9193ca 100644
--- a/apps/meteor/client/views/account/security/EndToEnd.tsx
+++ b/apps/meteor/client/views/account/security/EndToEnd.tsx
@@ -78,7 +78,13 @@ const EndToEnd = (props: ComponentProps<typeof Box>): ReactElement => {
 					<Field>
 						<Field.Label>{t('New_encryption_password')}</Field.Label>
 						<Field.Row>
-							<PasswordInput value={password} onChange={handlePassword} placeholder={t('New_Password_Placeholder')} disabled={!keysExist} />
+							<PasswordInput
+								value={password}
+								onChange={handlePassword}
+								placeholder={t('New_Password_Placeholder')}
+								disabled={!keysExist}
+								data-qa-type='e2e-encryption-password'
+							/>
 						</Field.Row>
 						{!keysExist && <Field.Hint>{t('EncryptionKey_Change_Disabled')}</Field.Hint>}
 					</Field>
@@ -90,19 +96,22 @@ const EndToEnd = (props: ComponentProps<typeof Box>): ReactElement => {
 								value={passwordConfirm}
 								onChange={handlePasswordConfirm}
 								placeholder={t('Confirm_New_Password_Placeholder')}
+								data-qa-type='e2e-encryption-password-confirmation'
 							/>
 							<Field.Error>{passwordError}</Field.Error>
 						</Field>
 					)}
 				</FieldGroup>
-				<Button primary disabled={!canSave} onClick={saveNewPassword}>
+				<Button primary disabled={!canSave} onClick={saveNewPassword} data-qa-type='e2e-encryption-save-password-button'>
 					{t('Save_changes')}
 				</Button>
 				<Box fontScale='h4' mbs='x16'>
 					{t('Reset_E2E_Key')}
 				</Box>
 				<Box dangerouslySetInnerHTML={{ __html: t('E2E_Reset_Key_Explanation') }} />
-				<Button onClick={handleResetE2eKey}>{t('Reset_E2E_Key')}</Button>
+				<Button onClick={handleResetE2eKey} data-qa-type='e2e-encryption-reset-key-button'>
+					{t('Reset_E2E_Key')}
+				</Button>
 			</Margins>
 		</Box>
 	);
diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts
index 1b20efdca6e82d739a91ccd064ac4d569591f951..f381c9b9fc3962a55249f5eabc819f1c12d4e851 100644
--- a/apps/meteor/lib/callbacks.ts
+++ b/apps/meteor/lib/callbacks.ts
@@ -177,6 +177,12 @@ type Hook =
 	| 'cachedCollection-received-subscriptions'
 	| 'cachedCollection-sync-rooms'
 	| 'cachedCollection-sync-subscriptions'
+	| 'cachedCollection-after-loadFromServer-rooms'
+	| 'cachedCollection-after-loadFromServer-subscriptions'
+	| 'cachedCollection-after-received-rooms'
+	| 'cachedCollection-after-received-subscriptions'
+	| 'cachedCollection-after-sync-rooms'
+	| 'cachedCollection-after-sync-subscriptions'
 	| 'enter-room'
 	| 'livechat.beforeForwardRoomToDepartment'
 	| 'livechat.beforeInquiry'
diff --git a/apps/meteor/tests/e2e/README.md b/apps/meteor/tests/e2e/README.md
index ea47c2bf1b3f783df3291d11f1f878973505a171..28d6c440eaaad6220566390894b471d04bb66715 100644
--- a/apps/meteor/tests/e2e/README.md
+++ b/apps/meteor/tests/e2e/README.md
@@ -21,7 +21,7 @@ $ yarn test:e2e
 
 We can also provide some env vars to `test:e2e` script:
 - `BASE_URL=<any_url>` Run the tests to the given url
-- `PWDEBUG=1` Controll the test execution
+- `PWDEBUG=1` Control the test execution
 
 ## Page Objects
 - Any locator name must start with of one the following prefixes: `btn`, `link`, `input`, `select`, `checkbox`, `text`
diff --git a/apps/meteor/tests/e2e/e2e-encryption.spec.ts b/apps/meteor/tests/e2e/e2e-encryption.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..74483a058842985490d9dca9a3155efefbf77371
--- /dev/null
+++ b/apps/meteor/tests/e2e/e2e-encryption.spec.ts
@@ -0,0 +1,199 @@
+import { faker } from '@faker-js/faker';
+import type { Page } from '@playwright/test';
+
+import { test, expect } from './utils/test';
+import { AccountProfile, HomeChannel } from './page-objects';
+import * as constants from './config/constants';
+
+// OK Enable e2ee on admin
+// OK Test banner and check password, logout and use password
+// OK Set new password, logout and use the password
+// OK Reset key, should logout, login and check banner
+// OK Create channel encrypted and send message
+// OK Disable encryption and send message
+// OK Enable encryption and send message
+// OK Create channel not encrypted, encrypt end send message
+
+async function login(page: Page): Promise<void> {
+	// TODO: Reuse code from global-setup.ts file
+	await page.locator('[name=username]').type(constants.ADMIN_CREDENTIALS.email);
+	await page.locator('[name=password]').type(constants.ADMIN_CREDENTIALS.password);
+	await page.locator('role=button >> text="Login"').click();
+	await page.waitForTimeout(1000);
+
+	await page.context().storageState({ path: `admin-session.json` });
+}
+
+test.use({ storageState: 'admin-session.json' });
+
+test.describe.serial('e2e-encryption initial setup', () => {
+	let poAccountProfile: AccountProfile;
+	let poHomeChannel: HomeChannel;
+	let password: string;
+
+	test.beforeEach(async ({ page }) => {
+		poAccountProfile = new AccountProfile(page);
+		poHomeChannel = new HomeChannel(page);
+
+		await page.goto('/account/security');
+	});
+
+	test.beforeAll(async ({ api }) => {
+		const statusCode = (await api.post('/settings/E2E_Enable', { value: true })).status();
+
+		expect(statusCode).toBe(200);
+	});
+
+	test("expect reset user's e2e encryption key", async ({ page }) => {
+		// Reset key to start the flow from the beginning
+		// It will execute a logout
+		await poAccountProfile.securityE2EEncryptionSection.click();
+		await poAccountProfile.securityE2EEncryptionResetKeyButton.click();
+
+		// Login again, check the banner to save the generated password and test it
+		await login(page);
+		await page.locator('role=banner >> text="Save Your Encryption Password"').click();
+
+		password = (await page.evaluate(() => localStorage.getItem('e2e.randomPassword'))) || 'undefined';
+
+		await expect(page.locator('#modal-root')).toContainText(password);
+
+		await page.locator('#modal-root .rcx-button--primary').click();
+
+		await expect(page.locator('role=banner >> text="Save Your Encryption Password"')).not.toBeVisible();
+
+		await poHomeChannel.sidenav.logout();
+
+		await login(page);
+
+		await page.locator('role=banner >> text="Enter your E2E password"').click();
+
+		await page.locator('#modal-root input').type(password);
+
+		await page.locator('#modal-root .rcx-button--primary').click();
+
+		await expect(page.locator('role=banner')).not.toBeVisible();
+
+		// Store the generated key
+		await page.context().storageState({ path: `admin-session.json` });
+	});
+
+	test('expect change the e2ee password', async ({ page }) => {
+		// Change the password to a new one and test it
+		const newPassword = 'new password';
+
+		await poAccountProfile.securityE2EEncryptionSection.click();
+		await poAccountProfile.securityE2EEncryptionPassword.click();
+		await poAccountProfile.securityE2EEncryptionPassword.type(newPassword);
+		await poAccountProfile.securityE2EEncryptionPasswordConfirmation.type(newPassword);
+		await poAccountProfile.securityE2EEncryptionSavePasswordButton.click();
+
+		await poAccountProfile.btnClose.click();
+
+		await poHomeChannel.sidenav.logout();
+
+		await login(page);
+
+		await page.locator('role=banner >> text="Enter your E2E password"').click();
+
+		await page.locator('#modal-root input').type(password);
+
+		await page.locator('#modal-root .rcx-button--primary').click();
+
+		await page.locator('role=banner >> text="Wasn\'t possible to decode your encryption key to be imported."').click();
+
+		await page.locator('#modal-root input').type(newPassword);
+
+		await page.locator('#modal-root .rcx-button--primary').click();
+
+		await expect(page.locator('role=banner')).not.toBeVisible();
+
+		// Store the current key
+		await page.context().storageState({ path: `admin-session.json` });
+	});
+});
+
+test.describe.serial('e2e-encryption', () => {
+	let poHomeChannel: HomeChannel;
+
+	test.beforeEach(async ({ page }) => {
+		poHomeChannel = new HomeChannel(page);
+
+		await page.goto('/home');
+		// TODO: remove
+		// await page.evaluate(() => localStorage.setItem('rc-config-debug', 'true'));
+		// TODO: remove block
+		// await page.locator('role=banner >> text="Enter your E2E password"').click();
+		// await page.locator('#modal-root input').type('new password');
+		// await page.locator('#modal-root .rcx-button--primary').click();
+		// await expect(page.locator('role=banner')).not.toBeVisible();
+	});
+
+	test('expect create a private channel encrypted and send an encrypted message', async ({ page }) => {
+		const channelName = faker.datatype.uuid();
+
+		await poHomeChannel.sidenav.openNewByLabel('Channel');
+		await poHomeChannel.sidenav.inputChannelName.type(channelName);
+		await poHomeChannel.sidenav.checkboxEncryption.click();
+		await poHomeChannel.sidenav.btnCreate.click();
+
+		await expect(page).toHaveURL(`/group/${channelName}`);
+
+		await expect(poHomeChannel.toastSuccess).toBeVisible();
+
+		await poHomeChannel.toastSuccess.locator('button >> i.rcx-icon--name-cross.rcx-icon').click();
+
+		await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();
+
+		await poHomeChannel.content.sendMessage('hello world');
+
+		await expect(poHomeChannel.content.lastUserMessage.locator('p')).toHaveText('hello world');
+		await expect(poHomeChannel.content.lastUserMessage.locator('.rcx-icon--name-key')).toBeVisible();
+
+		await poHomeChannel.tabs.kebab.click({ force: true });
+		await expect(poHomeChannel.tabs.btnE2E).toBeVisible();
+		await poHomeChannel.tabs.btnE2E.click({ force: true });
+		await page.waitForTimeout(1000);
+
+		await poHomeChannel.content.sendMessage('hello world not encrypted');
+
+		await expect(poHomeChannel.content.lastUserMessage.locator('p')).toHaveText('hello world not encrypted');
+		await expect(poHomeChannel.content.lastUserMessage.locator('.rcx-icon--name-key')).not.toBeVisible();
+
+		await poHomeChannel.tabs.kebab.click({ force: true });
+		await expect(poHomeChannel.tabs.btnE2E).toBeVisible();
+		await poHomeChannel.tabs.btnE2E.click({ force: true });
+		await page.waitForTimeout(1000);
+
+		await poHomeChannel.content.sendMessage('hello world encrypted again');
+
+		await expect(poHomeChannel.content.lastUserMessage.locator('p')).toHaveText('hello world encrypted again');
+		await expect(poHomeChannel.content.lastUserMessage.locator('.rcx-icon--name-key')).toBeVisible();
+	});
+
+	test('expect create a private channel, encrypt it and send an encrypted message', async ({ page }) => {
+		const channelName = faker.datatype.uuid();
+
+		await poHomeChannel.sidenav.openNewByLabel('Channel');
+		await poHomeChannel.sidenav.inputChannelName.type(channelName);
+		await poHomeChannel.sidenav.btnCreate.click();
+
+		await expect(page).toHaveURL(`/group/${channelName}`);
+
+		await expect(poHomeChannel.toastSuccess).toBeVisible();
+
+		await poHomeChannel.toastSuccess.locator('button >> i.rcx-icon--name-cross.rcx-icon').click();
+
+		await poHomeChannel.tabs.kebab.click({ force: true });
+		await expect(poHomeChannel.tabs.btnE2E).toBeVisible();
+		await poHomeChannel.tabs.btnE2E.click({ force: true });
+		await page.waitForTimeout(1000);
+
+		await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();
+
+		await poHomeChannel.content.sendMessage('hello world');
+
+		await expect(poHomeChannel.content.lastUserMessage.locator('p')).toHaveText('hello world');
+		await expect(poHomeChannel.content.lastUserMessage.locator('.rcx-icon--name-key')).toBeVisible();
+	});
+});
diff --git a/apps/meteor/tests/e2e/page-objects/account-profile.ts b/apps/meteor/tests/e2e/page-objects/account-profile.ts
index 3c2ed3087dec68fcd72be61aacafb99d0e8c4792..32545474c09c1a9dc245307edfd83080bfa13a80 100644
--- a/apps/meteor/tests/e2e/page-objects/account-profile.ts
+++ b/apps/meteor/tests/e2e/page-objects/account-profile.ts
@@ -41,7 +41,7 @@ export class AccountProfile {
 	}
 
 	get btnClose(): Locator {
-		return this.page.locator('button >> i.rcx-icon--name-cross.rcx-icon');
+		return this.page.locator('aside[role="navigation"] button >> i.rcx-icon--name-cross.rcx-icon');
 	}
 
 	get inputToken(): Locator {
@@ -75,4 +75,24 @@ export class AccountProfile {
 	get inputImageFile(): Locator {
 		return this.page.locator('input[type=file]');
 	}
+
+	get securityE2EEncryptionSection(): Locator {
+		return this.page.locator("[data-qa-type='e2e-encryption-section']");
+	}
+
+	get securityE2EEncryptionResetKeyButton(): Locator {
+		return this.page.locator("[data-qa-type='e2e-encryption-reset-key-button']");
+	}
+
+	get securityE2EEncryptionPassword(): Locator {
+		return this.page.locator("[data-qa-type='e2e-encryption-password']");
+	}
+
+	get securityE2EEncryptionPasswordConfirmation(): Locator {
+		return this.page.locator("[data-qa-type='e2e-encryption-password-confirmation']");
+	}
+
+	get securityE2EEncryptionSavePasswordButton(): Locator {
+		return this.page.locator("[data-qa-type='e2e-encryption-save-password-button']");
+	}
 }
diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts
index b64080bb9f3be2f77415f03c2fe28a153bfe2358..fd0a1ee834845a9cfe0d699c6a58a2aec26b10f8 100644
--- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts
+++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts
@@ -25,6 +25,10 @@ export class HomeContent {
 		return this.page.locator('[data-qa-type="message"][data-sequential="false"]').last();
 	}
 
+	get encryptedRoomHeaderIcon(): Locator {
+		return this.page.locator('.rcx-room-header button > i.rcx-icon--name-key');
+	}
+
 	async sendMessage(text: string): Promise<void> {
 		await this.page.locator('[name="msg"]').type(text);
 		await this.page.keyboard.press('Enter');
diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts
index 0dd13b775aacfd647092ad353445bbe55561d44a..c50c31c06180c8ae4aa913ea8ddf09f94fb97cdb 100644
--- a/apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts
+++ b/apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts
@@ -44,6 +44,10 @@ export class HomeFlextab {
 		return this.page.locator('[data-qa-id=ToolBoxAction-bell]');
 	}
 
+	get btnE2E(): Locator {
+		return this.page.locator('[data-qa-id=ToolBoxAction-key]');
+	}
+
 	get flexTabViewThreadMessage(): Locator {
 		return this.page
 			.locator('div.thread-list ul.thread [data-qa-type="message"]')
diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts
index 200582a5a756c8f77a9156f2ec2735fa50fe4ccd..83bb0cc68840afdda445293053d52323d72a8208 100644
--- a/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts
+++ b/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts
@@ -10,9 +10,11 @@ export class HomeSidenav {
 	}
 
 	get checkboxPrivateChannel(): Locator {
-		return this.page.locator(
-			'//*[@id="modal-root"]//*[contains(@class, "rcx-field") and contains(text(), "Private")]/../following-sibling::label/i',
-		);
+		return this.page.locator('#modal-root [data-qa="create-channel-modal"] [data-qa-type="channel-private-toggle"]');
+	}
+
+	get checkboxEncryption(): Locator {
+		return this.page.locator('#modal-root [data-qa="create-channel-modal"] [data-qa-type="encryption-toggle"]');
 	}
 
 	get inputChannelName(): Locator {