diff --git a/changelog.md b/changelog.md
index 068f338168..665748398f 100644
--- a/changelog.md
+++ b/changelog.md
@@ -6,6 +6,7 @@ Core:
 
 - Allow to leave workspace
 - Allow to kick employee
+- Browser notifications
 - Allow to create employee
 
 HR:
diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts
index 76ce6e4b40..29f6dd6199 100644
--- a/models/notification/src/index.ts
+++ b/models/notification/src/index.ts
@@ -51,6 +51,10 @@ export class TNotification extends TAttachedDoc implements Notification {
 
   @Prop(TypeString(), 'Status' as IntlString)
   status!: NotificationStatus
+
+  text!: string
+
+  type!: Ref<NotificationType>
 }
 
 @Model(notification.class.EmailNotification, core.class.Doc, DOMAIN_NOTIFICATION)
@@ -137,6 +141,16 @@ export function createModel (builder: Builder): void {
     notification.ids.PlatformNotification
   )
 
+  builder.createDoc(
+    notification.class.NotificationProvider,
+    core.space.Model,
+    {
+      label: notification.string.BrowserNotification,
+      default: false
+    },
+    notification.ids.BrowserNotification
+  )
+
   builder.createDoc(
     notification.class.NotificationProvider,
     core.space.Model,
diff --git a/models/notification/src/migration.ts b/models/notification/src/migration.ts
index 643c7a5d6a..bf6fbbee68 100644
--- a/models/notification/src/migration.ts
+++ b/models/notification/src/migration.ts
@@ -13,9 +13,56 @@
 // limitations under the License.
 //
 
+import core, { DOMAIN_TX, Ref, TxCreateDoc, TxOperations } from '@anticrm/core'
 import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
+import notification, { Notification, NotificationType } from '@anticrm/notification'
+import { DOMAIN_NOTIFICATION } from '.'
+
+async function fillNotificationText (client: MigrationClient): Promise<void> {
+  await client.update(
+    DOMAIN_NOTIFICATION,
+    { _class: notification.class.Notification, text: { $exists: false } },
+    {
+      text: ''
+    }
+  )
+  await client.update(
+    DOMAIN_TX,
+    {
+      _class: core.class.TxCreateDoc,
+      objectClass: notification.class.Notification,
+      'attributes.text': { $exists: false }
+    },
+    {
+      'attributes.text': ''
+    }
+  )
+}
+
+async function fillNotificationType (client: MigrationUpgradeClient): Promise<void> {
+  const notifications = await client.findAll(notification.class.Notification, { type: { $exists: false } })
+  const txOp = new TxOperations(client, core.account.System)
+  const promises = notifications.map(async (doc) => {
+    const tx = await client.findOne(core.class.TxCUD, { _id: doc.tx })
+    if (tx === undefined) return
+    const type =
+      tx._class === core.class.TxMixin
+        ? ('calendar:ids:ReminderNotification' as Ref<NotificationType>)
+        : notification.ids.MentionNotification
+    const objectTx = txOp.update(doc, { type })
+    const ctx = await client.findOne<TxCreateDoc<Notification>>(core.class.TxCreateDoc, { objectId: doc._id })
+    if (ctx === undefined) return await objectTx
+    const updateTx = txOp.update(ctx, { 'attributes.type': type } as any)
+    return await Promise.all([objectTx, updateTx])
+  })
+  await Promise.all(promises)
+}
 
 export const notificationOperation: MigrateOperation = {
-  async migrate (client: MigrationClient): Promise<void> {},
-  async upgrade (client: MigrationUpgradeClient): Promise<void> {}
+  async migrate (client: MigrationClient): Promise<void> {
+    await fillNotificationText(client)
+  },
+  async upgrade (client: MigrationUpgradeClient): Promise<void> {
+    await fillNotificationType(client)
+  }
 }
diff --git a/models/notification/src/plugin.ts b/models/notification/src/plugin.ts
index ba81088122..4d90fb72eb 100644
--- a/models/notification/src/plugin.ts
+++ b/models/notification/src/plugin.ts
@@ -23,6 +23,7 @@ export default mergeIds(notificationId, notification, {
     LastView: '' as IntlString,
     MentionNotification: '' as IntlString,
     PlatformNotification: '' as IntlString,
+    BrowserNotification: '' as IntlString,
     EmailNotification: '' as IntlString
   },
   component: {
diff --git a/plugins/notification-assets/lang/en.json b/plugins/notification-assets/lang/en.json
index b3dae1168a..74064664d1 100644
--- a/plugins/notification-assets/lang/en.json
+++ b/plugins/notification-assets/lang/en.json
@@ -8,6 +8,7 @@
     "EmailNotification": "by email",
     "PlatformNotification": "in platform",
     "Track": "Track",
-    "DontTrack": "Don't track"
+    "DontTrack": "Don't track",
+    "BrowserNotification": "in browser"
   }
 }
\ No newline at end of file
diff --git a/plugins/notification-assets/lang/ru.json b/plugins/notification-assets/lang/ru.json
index c773ab0590..37d64f395f 100644
--- a/plugins/notification-assets/lang/ru.json
+++ b/plugins/notification-assets/lang/ru.json
@@ -8,6 +8,7 @@
     "EmailNotification": "по email",
     "PlatformNotification": "в системе",
     "Track": "Отслеживать",
-    "DontTrack": "Не отслеживать"
+    "DontTrack": "Не отслеживать",
+    "BrowserNotification": "в браузере"
   }
 }
\ No newline at end of file
diff --git a/plugins/notification-resources/src/components/BrowserNotificatator.svelte b/plugins/notification-resources/src/components/BrowserNotificatator.svelte
new file mode 100644
index 0000000000..f9abd69007
--- /dev/null
+++ b/plugins/notification-resources/src/components/BrowserNotificatator.svelte
@@ -0,0 +1,119 @@
+<!--
+// Copyright © 2022 Hardcore Engineering Inc.
+//
+// Licensed under the Eclipse Public License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License. You may
+// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//
+// See the License for the specific language governing permissions and
+// limitations under the License.
+-->
+<script lang="ts">
+  import contact, { EmployeeAccount } from '@anticrm/contact'
+  import { Doc, getCurrentAccount, Ref, Space } from '@anticrm/core'
+  import {
+    Notification as PlatformNotification,
+    NotificationProvider,
+    NotificationSetting,
+    NotificationStatus,
+    NotificationType
+  } from '@anticrm/notification'
+  import { createQuery } from '@anticrm/presentation'
+  import notification from '../plugin'
+  import { NotificationClientImpl } from '../utils'
+
+  const query = createQuery()
+  const settingQuery = createQuery()
+  const providersQuery = createQuery()
+  const accountId = getCurrentAccount()._id
+  const space = accountId as string as Ref<Space>
+  const notificationClient = NotificationClientImpl.getClient()
+  const lastViews = notificationClient.getLastViews()
+  const lastViewId: Ref<Doc> = ((getCurrentAccount() as EmployeeAccount).employee + 'notification') as Ref<Doc>
+
+  let settingsReceived = false
+  let settings: Map<Ref<NotificationType>, NotificationSetting> = new Map<Ref<NotificationType>, NotificationSetting>()
+  let provider: NotificationProvider | undefined
+
+  const enabled = 'Notification' in window && Notification.permission !== 'denied'
+
+  $: enabled &&
+    providersQuery.query(
+      notification.class.NotificationProvider,
+      { _id: notification.ids.BrowserNotification },
+      (res) => {
+        provider = res[0]
+      }
+    )
+
+  $: enabled &&
+    settingQuery.query(
+      notification.class.NotificationSetting,
+      {
+        space
+      },
+      (res) => {
+        settings = new Map(
+          res.map((setting) => {
+            return [setting.type, setting]
+          })
+        )
+        settingsReceived = true
+      }
+    )
+
+  $: enabled &&
+    settingsReceived &&
+    provider !== undefined &&
+    query.query(
+      notification.class.Notification,
+      {
+        attachedTo: (getCurrentAccount() as EmployeeAccount).employee,
+        status: NotificationStatus.New
+      },
+      (res) => {
+        process(res)
+      }
+    )
+
+  async function process (notifications: PlatformNotification[]): Promise<void> {
+    for (const notification of notifications) {
+      await tryNotify(notification)
+    }
+  }
+
+  async function tryNotify (notification: PlatformNotification): Promise<void> {
+    const text = notification.text.replace(/<[^>]*>/g, '').trim()
+    if (text === '') return
+    const setting = settings.get(notification.type)
+    const enabled = setting?.enabled ?? provider?.default
+    if (!enabled) return
+    if (setting?.modifiedOn ?? notification.modifiedOn < 0) return
+    if (Notification.permission === 'granted') {
+      await notify(text, notification)
+    } else if (Notification.permission !== 'denied') {
+      const permission = await Notification.requestPermission()
+      if (permission === 'granted') {
+        await notify(text, notification)
+      }
+    }
+  }
+
+  async function notify (text: string, notification: PlatformNotification): Promise<void> {
+    const lastView = $lastViews.get(lastViewId)
+    if (lastView ?? notification.modifiedOn > 0) {
+      // eslint-disable-next-line
+      new Notification(text, { tag: notification._id })
+      await notificationClient.updateLastView(
+        lastViewId,
+        contact.class.Employee,
+        notification.modifiedOn,
+        lastView === undefined
+      )
+    }
+  }
+</script>
diff --git a/plugins/notification-resources/src/components/NotificationSettings.svelte b/plugins/notification-resources/src/components/NotificationSettings.svelte
index bbaf3c693e..b28f1d73c6 100644
--- a/plugins/notification-resources/src/components/NotificationSettings.svelte
+++ b/plugins/notification-resources/src/components/NotificationSettings.svelte
@@ -26,6 +26,8 @@
   const client = getClient()
   const space = accountId as string as Ref<Space>
 
+  let disabled = true
+
   let types: NotificationType[] = []
   let providers: NotificationProvider[] = []
   let settings: Map<Ref<NotificationType>, Map<Ref<NotificationProvider>, NotificationSetting>> = new Map<
@@ -67,6 +69,7 @@
     } else {
       current.enabled = value
     }
+    disabled = false
   }
 
   function getSetting (
@@ -92,6 +95,7 @@
   }
 
   async function save (): Promise<void> {
+    disabled = true
     const promises: Promise<any>[] = []
     for (const type of settings.values()) {
       for (const setting of type.values()) {
@@ -107,7 +111,11 @@
         }
       }
     }
-    await Promise.all(promises)
+    try {
+      await Promise.all(promises)
+    } catch (e) {
+      console.log(e)
+    }
   }
 
   $: column = providers.length + 1
@@ -152,6 +160,7 @@
       <div class="flex-row-reverse">
         <Button
           label={presentation.string.Save}
+          {disabled}
           kind={'primary'}
           on:click={() => {
             save()
diff --git a/plugins/notification-resources/src/index.ts b/plugins/notification-resources/src/index.ts
index d437af8bfe..827dc7dc54 100644
--- a/plugins/notification-resources/src/index.ts
+++ b/plugins/notification-resources/src/index.ts
@@ -23,6 +23,8 @@ import { NotificationClientImpl } from './utils'
 
 export * from './utils'
 
+export { default as BrowserNotificatator } from './components/BrowserNotificatator.svelte'
+
 export default async (): Promise<Resources> => ({
   component: {
     NotificationsPopup,
diff --git a/plugins/notification/src/index.ts b/plugins/notification/src/index.ts
index 98318c3596..7f44ef4743 100644
--- a/plugins/notification/src/index.ts
+++ b/plugins/notification/src/index.ts
@@ -33,6 +33,8 @@ export interface LastView extends AttachedDoc {
 export interface Notification extends AttachedDoc {
   tx: Ref<TxCUD<Doc>>
   status: NotificationStatus
+  text: string
+  type: Ref<NotificationType>
 }
 
 /**
@@ -137,6 +139,7 @@ const notification = plugin(notificationId, {
   ids: {
     MentionNotification: '' as Ref<NotificationType>,
     PlatformNotification: '' as Ref<NotificationProvider>,
+    BrowserNotification: '' as Ref<NotificationProvider>,
     EmailNotification: '' as Ref<NotificationProvider>,
     NotificationSettings: '' as Ref<Doc>
   },
diff --git a/plugins/workbench-resources/src/components/Workbench.svelte b/plugins/workbench-resources/src/components/Workbench.svelte
index dd99a2a655..c3139febb9 100644
--- a/plugins/workbench-resources/src/components/Workbench.svelte
+++ b/plugins/workbench-resources/src/components/Workbench.svelte
@@ -17,7 +17,7 @@
   import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
   import core, { Class, Client, Doc, getCurrentAccount, Ref, Space } from '@anticrm/core'
   import notification, { NotificationStatus } from '@anticrm/notification'
-  import { NotificationClientImpl } from '@anticrm/notification-resources'
+  import { NotificationClientImpl, BrowserNotificatator } from '@anticrm/notification-resources'
   import { getMetadata, getResource, IntlString } from '@anticrm/platform'
   import { Avatar, createQuery, setClient } from '@anticrm/presentation'
   import {
@@ -119,9 +119,6 @@
     },
     (res) => {
       hasNotification = res.length > 0
-    },
-    {
-      limit: 1
     }
   )
 
@@ -517,6 +514,7 @@
     </svelte:fragment>
   </Popup>
   <DatePickerPopup />
+  <BrowserNotificatator />
 {:else}
   <div class="flex-col-center justify-center h-full flex-grow">
     <h1><Label label={workbench.string.AccountDisabled} /></h1>
diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts
index 0607a8b627..17e9426343 100644
--- a/server-plugins/notification-resources/src/index.ts
+++ b/server-plugins/notification-resources/src/index.ts
@@ -34,7 +34,12 @@ import core, {
   TxCUD,
   TxProcessor
 } from '@anticrm/core'
-import notification, { EmailNotification, Notification, NotificationStatus } from '@anticrm/notification'
+import notification, {
+  EmailNotification,
+  Notification,
+  NotificationProvider,
+  NotificationStatus
+} from '@anticrm/notification'
 import { getResource } from '@anticrm/platform'
 import type { TriggerControl } from '@anticrm/server-core'
 import { extractTx } from '@anticrm/server-core'
@@ -46,33 +51,52 @@ import view, { HTMLPresenter, TextPresenter } from '@anticrm/view'
  */
 export async function OnBacklinkCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
   const hierarchy = control.hierarchy
-  if (tx._class !== core.class.TxCollectionCUD) {
-    return []
+  const ptx = tx as TxCollectionCUD<Doc, Backlink>
+
+  if (!checkTx(ptx, hierarchy)) return []
+
+  const result: Tx[] = []
+
+  const receiver = await getReceiver(ptx, control)
+  if (receiver === undefined) return []
+  const sender = await getSender(ptx, control)
+  const backlink = getBacklink(ptx)
+  const doc = await getBacklinkDoc(backlink, control)
+  const textPart = doc !== undefined ? await getTextPart(doc, hierarchy) : undefined
+  const htmlPart = doc !== undefined ? await getHtmlPart(doc, hierarchy) : undefined
+
+  const createNotificationTx = await getPlatformNotificationTx(ptx, backlink, textPart, sender)
+
+  if (createNotificationTx !== undefined) {
+    result.push(createNotificationTx)
   }
 
-  const ptx = tx as TxCollectionCUD<Doc, Backlink>
+  if (
+    sender !== undefined &&
+    textPart !== undefined &&
+    (await isAllowed(control, receiver, notification.ids.EmailNotification))
+  ) {
+    const emailTx = await getEmailTx(ptx, backlink, sender, textPart, htmlPart, receiver)
+    if (emailTx !== undefined) {
+      result.push(emailTx)
+    }
+  }
+  return result
+}
+
+function checkTx (ptx: TxCollectionCUD<Doc, Backlink>, hierarchy: Hierarchy): boolean {
+  if (ptx._class !== core.class.TxCollectionCUD) {
+    return false
+  }
 
   if (
     ptx.tx._class !== core.class.TxCreateDoc ||
     !hierarchy.isDerived(ptx.tx.objectClass, chunter.class.Backlink) ||
     !hierarchy.isDerived(ptx.objectClass, contact.class.Employee)
   ) {
-    return []
+    return false
   }
-
-  const result: Tx[] = []
-
-  const createNotificationTx = await getPlatformNotificationTx(ptx, control)
-
-  if (createNotificationTx !== undefined) {
-    result.push(createNotificationTx)
-  }
-
-  const emailTx = await getEmailTx(ptx, control)
-  if (emailTx !== undefined) {
-    result.push(emailTx)
-  }
-  return result
+  return true
 }
 
 async function getUpdateLastViewTxes (
@@ -170,11 +194,11 @@ export async function UpdateLastView (tx: Tx, control: TriggerControl): Promise<
   return result
 }
 
-async function getPlatformNotificationTx (
+async function getReceiver (
   ptx: TxCollectionCUD<Doc, Backlink>,
   control: TriggerControl
-): Promise<TxCollectionCUD<Doc, Notification> | undefined> {
-  const attached = (
+): Promise<EmployeeAccount | undefined> {
+  return (
     await control.modelDb.findAll(
       contact.class.EmployeeAccount,
       {
@@ -183,29 +207,42 @@ async function getPlatformNotificationTx (
       { limit: 1 }
     )
   )[0]
-  if (attached === undefined) return
+}
 
+async function isAllowed (
+  control: TriggerControl,
+  receiver: EmployeeAccount,
+  providerId: Ref<NotificationProvider>
+): Promise<boolean> {
   const setting = (
     await control.findAll(
       notification.class.NotificationSetting,
       {
-        provider: notification.ids.PlatformNotification,
+        provider: providerId,
         type: notification.ids.MentionNotification,
-        space: attached._id as unknown as Ref<Space>
+        space: receiver._id as unknown as Ref<Space>
       },
       { limit: 1 }
     )
   )[0]
-  if (setting === undefined) {
-    const provider = (
-      await control.modelDb.findAll(notification.class.NotificationProvider, {
-        _id: notification.ids.PlatformNotification
-      })
-    )[0]
-    if (provider === undefined) return
-    if (!provider.default) return
+  if (setting !== undefined) {
+    return setting.enabled
   }
+  const provider = (
+    await control.modelDb.findAll(notification.class.NotificationProvider, {
+      _id: providerId
+    })
+  )[0]
+  if (provider === undefined) return false
+  return provider.default
+}
 
+async function getPlatformNotificationTx (
+  ptx: TxCollectionCUD<Doc, Backlink>,
+  backlink: Backlink,
+  textPart: string | undefined,
+  sender: string | undefined
+): Promise<TxCollectionCUD<Doc, Notification> | undefined> {
   const createTx: TxCreateDoc<Notification> = {
     objectClass: notification.class.Notification,
     objectSpace: notification.space.Notifications,
@@ -217,10 +254,16 @@ async function getPlatformNotificationTx (
     _class: core.class.TxCreateDoc,
     attributes: {
       tx: ptx._id,
-      status: NotificationStatus.New
+      status: NotificationStatus.New,
+      type: notification.ids.MentionNotification
     } as unknown as Data<Notification>
   }
 
+  if (sender !== undefined && textPart !== undefined) {
+    const text = `${sender} mentioned you in ${textPart} ${backlink.message}`
+    createTx.attributes.text = text
+  }
+
   const createNotificationTx: TxCollectionCUD<Doc, Notification> = {
     ...ptx,
     _id: generateId(),
@@ -231,12 +274,35 @@ async function getPlatformNotificationTx (
   return createNotificationTx
 }
 
-async function getEmailTx (
-  ptx: TxCollectionCUD<Doc, Backlink>,
-  control: TriggerControl
-): Promise<TxCreateDoc<EmailNotification> | undefined> {
-  const hierarchy = control.hierarchy
-  const backlink = TxProcessor.createDoc2Doc(ptx.tx as TxCreateDoc<Backlink>)
+function getBacklink (ptx: TxCollectionCUD<Doc, Backlink>): Backlink {
+  return TxProcessor.createDoc2Doc(ptx.tx as TxCreateDoc<Backlink>)
+}
+
+async function getBacklinkDoc (backlink: Backlink, control: TriggerControl): Promise<Doc | undefined> {
+  return (
+    await control.findAll(
+      backlink.backlinkClass,
+      {
+        _id: backlink.backlinkId
+      },
+      { limit: 1 }
+    )
+  )[0]
+}
+
+async function getTextPart (doc: Doc, hierarchy: Hierarchy): Promise<string | undefined> {
+  const TextPresenter = getTextPresenter(doc._class, hierarchy)
+  if (TextPresenter === undefined) return
+  return (await getResource(TextPresenter.presenter))(doc)
+}
+
+async function getHtmlPart (doc: Doc, hierarchy: Hierarchy): Promise<string | undefined> {
+  const HTMLPresenter = getHTMLPresenter(doc._class, hierarchy)
+  const htmlPart = HTMLPresenter !== undefined ? (await getResource(HTMLPresenter.presenter))(doc) : undefined
+  return htmlPart
+}
+
+async function getSender (ptx: TxCollectionCUD<Doc, Backlink>, control: TriggerControl): Promise<string | undefined> {
   const account = (
     await control.modelDb.findAll(
       contact.class.EmployeeAccount,
@@ -248,57 +314,17 @@ async function getEmailTx (
   )[0]
   if (account === undefined) return undefined
 
-  const sender = formatName(account.name)
-  const attached = (
-    await control.modelDb.findAll(
-      contact.class.EmployeeAccount,
-      {
-        employee: ptx.objectId as Ref<Employee>
-      },
-      { limit: 1 }
-    )
-  )[0]
-  if (attached === undefined) return undefined
+  return formatName(account.name)
+}
 
-  const setting = (
-    await control.findAll(
-      notification.class.NotificationSetting,
-      {
-        provider: notification.ids.EmailNotification,
-        type: notification.ids.MentionNotification,
-        space: attached._id as unknown as Ref<Space>
-      },
-      { limit: 1 }
-    )
-  )[0]
-  if (setting === undefined) {
-    const provider = (
-      await control.modelDb.findAll(notification.class.NotificationProvider, {
-        _id: notification.ids.PlatformNotification
-      })
-    )[0]
-    if (provider === undefined) return
-    if (!provider.default) return
-  }
-
-  const receiver = attached.email
-  const doc = (
-    await control.findAll(
-      backlink.backlinkClass,
-      {
-        _id: backlink.backlinkId
-      },
-      { limit: 1 }
-    )
-  )[0]
-  if (doc === undefined) return undefined
-
-  const TextPresenter = getTextPresenter(doc._class, hierarchy)
-  if (TextPresenter === undefined) return
-
-  const HTMLPresenter = getHTMLPresenter(doc._class, hierarchy)
-  const htmlPart = HTMLPresenter !== undefined ? (await getResource(HTMLPresenter.presenter))(doc) : undefined
-  const textPart = (await getResource(TextPresenter.presenter))(doc)
+async function getEmailTx (
+  ptx: TxCollectionCUD<Doc, Backlink>,
+  backlink: Backlink,
+  sender: string,
+  textPart: string,
+  htmlPart: string | undefined,
+  receiver: EmployeeAccount
+): Promise<TxCreateDoc<EmailNotification> | undefined> {
   const html = `<p><b>${sender}</b> mentioned you in ${htmlPart !== undefined ? htmlPart : textPart}</p> ${
     backlink.message
   }`
@@ -315,7 +341,7 @@ async function getEmailTx (
     attributes: {
       status: 'new',
       sender,
-      receivers: [receiver],
+      receivers: [receiver.email],
       subject: `You was mentioned in ${textPart}`,
       text,
       html