diff --git a/src/api/admin.rs b/src/api/admin.rs
index 6c908bfc..b108b24a 100644
--- a/src/api/admin.rs
+++ b/src/api/admin.rs
@@ -13,7 +13,7 @@ use rocket::{
 };
 
 use crate::{
-    api::{core::log_event, ApiResult, EmptyResult, JsonResult, NumberOrString},
+    api::{core::log_event, ApiResult, EmptyResult, JsonResult, Notify, NumberOrString, UpdateType},
     auth::{decode_admin, encode_jwt, generate_admin_claims, ClientIp},
     config::ConfigBuilder,
     db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
@@ -380,22 +380,30 @@ async fn delete_user(uuid: String, _token: AdminToken, mut conn: DbConn, ip: Cli
 }
 
 #[post("/users/<uuid>/deauth")]
-async fn deauth_user(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
+async fn deauth_user(uuid: String, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
     let mut user = get_user_or_404(&uuid, &mut conn).await?;
     Device::delete_all_by_user(&user.uuid, &mut conn).await?;
     user.reset_security_stamp();
 
-    user.save(&mut conn).await
+    let save_result = user.save(&mut conn).await;
+
+    nt.send_user_update(UpdateType::LogOut, &user).await;
+
+    save_result
 }
 
 #[post("/users/<uuid>/disable")]
-async fn disable_user(uuid: String, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
+async fn disable_user(uuid: String, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
     let mut user = get_user_or_404(&uuid, &mut conn).await?;
     Device::delete_all_by_user(&user.uuid, &mut conn).await?;
     user.reset_security_stamp();
     user.enabled = false;
 
-    user.save(&mut conn).await
+    let save_result = user.save(&mut conn).await;
+
+    nt.send_user_update(UpdateType::LogOut, &user).await;
+
+    save_result
 }
 
 #[post("/users/<uuid>/enable")]
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index 3315fbce..31094499 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -275,6 +275,7 @@ async fn post_password(
     headers: Headers,
     mut conn: DbConn,
     ip: ClientIp,
+    nt: Notify<'_>,
 ) -> EmptyResult {
     let data: ChangePassData = data.into_inner().data;
     let mut user = headers.user;
@@ -293,7 +294,11 @@ async fn post_password(
         Some(vec![String::from("post_rotatekey"), String::from("get_contacts"), String::from("get_public_keys")]),
     );
     user.akey = data.Key;
-    user.save(&mut conn).await
+    let save_result = user.save(&mut conn).await;
+
+    nt.send_user_update(UpdateType::LogOut, &user).await;
+
+    save_result
 }
 
 #[derive(Deserialize)]
@@ -308,7 +313,7 @@ struct ChangeKdfData {
 }
 
 #[post("/accounts/kdf", data = "<data>")]
-async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
+async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
     let data: ChangeKdfData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -320,7 +325,11 @@ async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: D
     user.client_kdf_type = data.Kdf;
     user.set_password(&data.NewMasterPasswordHash, None);
     user.akey = data.Key;
-    user.save(&mut conn).await
+    let save_result = user.save(&mut conn).await;
+
+    nt.send_user_update(UpdateType::LogOut, &user).await;
+
+    save_result
 }
 
 #[derive(Deserialize)]
@@ -388,6 +397,7 @@ async fn post_rotatekey(
 
         // Prevent triggering cipher updates via WebSockets by settings UpdateType::None
         // The user sessions are invalidated because all the ciphers were re-encrypted and thus triggering an update could cause issues.
+        // We force the users to logout after the user has been saved to try and prevent these issues.
         update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &mut conn, &ip, &nt, UpdateType::None)
             .await?
     }
@@ -399,11 +409,20 @@ async fn post_rotatekey(
     user.private_key = Some(data.PrivateKey);
     user.reset_security_stamp();
 
-    user.save(&mut conn).await
+    let save_result = user.save(&mut conn).await;
+
+    nt.send_user_update(UpdateType::LogOut, &user).await;
+
+    save_result
 }
 
 #[post("/accounts/security-stamp", data = "<data>")]
-async fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
+async fn post_sstamp(
+    data: JsonUpcase<PasswordData>,
+    headers: Headers,
+    mut conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
     let data: PasswordData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -413,7 +432,11 @@ async fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, mut conn:
 
     Device::delete_all_by_user(&user.uuid, &mut conn).await?;
     user.reset_security_stamp();
-    user.save(&mut conn).await
+    let save_result = user.save(&mut conn).await;
+
+    nt.send_user_update(UpdateType::LogOut, &user).await;
+
+    save_result
 }
 
 #[derive(Deserialize)]
@@ -465,7 +488,12 @@ struct ChangeEmailData {
 }
 
 #[post("/accounts/email", data = "<data>")]
-async fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
+async fn post_email(
+    data: JsonUpcase<ChangeEmailData>,
+    headers: Headers,
+    mut conn: DbConn,
+    nt: Notify<'_>,
+) -> EmptyResult {
     let data: ChangeEmailData = data.into_inner().data;
     let mut user = headers.user;
 
@@ -507,8 +535,11 @@ async fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, mut con
 
     user.set_password(&data.NewMasterPasswordHash, None);
     user.akey = data.Key;
+    let save_result = user.save(&mut conn).await;
 
-    user.save(&mut conn).await
+    nt.send_user_update(UpdateType::LogOut, &user).await;
+
+    save_result
 }
 
 #[post("/accounts/verify-email")]
diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs
index c72419b0..463f5f0b 100644
--- a/src/api/core/ciphers.rs
+++ b/src/api/core/ciphers.rs
@@ -310,7 +310,8 @@ async fn post_ciphers(
     data.LastKnownRevisionDate = None;
 
     let mut cipher = Cipher::new(data.Type, data.Name.clone());
-    update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::CipherCreate).await?;
+    update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherCreate)
+        .await?;
 
     Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await))
 }
@@ -415,7 +416,14 @@ pub async fn update_cipher_from_data(
         for (id, attachment) in attachments {
             let mut saved_att = match Attachment::find_by_id(&id, conn).await {
                 Some(att) => att,
-                None => err!("Attachment doesn't exist"),
+                None => {
+                    // Warn and continue here.
+                    // A missing attachment means it was removed via an other client.
+                    // Also the Desktop Client supports removing attachments and save an update afterwards.
+                    // Bitwarden it self ignores these mismatches server side.
+                    warn!("Attachment {id} doesn't exist");
+                    continue;
+                }
             };
 
             if saved_att.cipher_uuid != cipher.uuid {
@@ -482,8 +490,8 @@ pub async fn update_cipher_from_data(
         // Only log events for organizational ciphers
         if let Some(org_uuid) = &cipher.organization_uuid {
             let event_type = match (&ut, transfer_cipher) {
-                (UpdateType::CipherCreate, true) => EventType::CipherCreated,
-                (UpdateType::CipherUpdate, true) => EventType::CipherShared,
+                (UpdateType::SyncCipherCreate, true) => EventType::CipherCreated,
+                (UpdateType::SyncCipherUpdate, true) => EventType::CipherShared,
                 (_, _) => EventType::CipherUpdated,
             };
 
@@ -499,7 +507,7 @@ pub async fn update_cipher_from_data(
             .await;
         }
 
-        nt.send_cipher_update(ut, cipher, &cipher.update_users_revision(conn).await).await;
+        nt.send_cipher_update(ut, cipher, &cipher.update_users_revision(conn).await, &headers.device.uuid).await;
     }
 
     Ok(())
@@ -562,7 +570,7 @@ async fn post_ciphers_import(
 
     let mut user = headers.user;
     user.update_revision(&mut conn).await?;
-    nt.send_user_update(UpdateType::Vault, &user).await;
+    nt.send_user_update(UpdateType::SyncVault, &user).await;
     Ok(())
 }
 
@@ -628,7 +636,8 @@ async fn put_cipher(
         err!("Cipher is not write accessible")
     }
 
-    update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::CipherUpdate).await?;
+    update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherUpdate)
+        .await?;
 
     Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await))
 }
@@ -850,9 +859,9 @@ async fn share_cipher_by_uuid(
 
     // When LastKnownRevisionDate is None, it is a new cipher, so send CipherCreate.
     let ut = if data.Cipher.LastKnownRevisionDate.is_some() {
-        UpdateType::CipherUpdate
+        UpdateType::SyncCipherUpdate
     } else {
-        UpdateType::CipherCreate
+        UpdateType::SyncCipherCreate
     };
 
     update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, ip, nt, ut).await?;
@@ -1067,7 +1076,13 @@ async fn save_attachment(
         data.data.move_copy_to(file_path).await?
     }
 
-    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&mut conn).await).await;
+    nt.send_cipher_update(
+        UpdateType::SyncCipherUpdate,
+        &cipher,
+        &cipher.update_users_revision(&mut conn).await,
+        &headers.device.uuid,
+    )
+    .await;
 
     if let Some(org_uuid) = &cipher.organization_uuid {
         log_event(
@@ -1403,7 +1418,7 @@ async fn move_cipher_selected(
         // Move cipher
         cipher.move_to_folder(data.FolderId.clone(), &user_uuid, &mut conn).await?;
 
-        nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &[user_uuid.clone()]).await;
+        nt.send_cipher_update(UpdateType::SyncCipherUpdate, &cipher, &[user_uuid.clone()], &headers.device.uuid).await;
     }
 
     Ok(())
@@ -1451,7 +1466,7 @@ async fn delete_all(
                 Some(user_org) => {
                     if user_org.atype == UserOrgType::Owner {
                         Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?;
-                        nt.send_user_update(UpdateType::Vault, &user).await;
+                        nt.send_user_update(UpdateType::SyncVault, &user).await;
 
                         log_event(
                             EventType::OrganizationPurgedVault as i32,
@@ -1484,7 +1499,7 @@ async fn delete_all(
             }
 
             user.update_revision(&mut conn).await?;
-            nt.send_user_update(UpdateType::Vault, &user).await;
+            nt.send_user_update(UpdateType::SyncVault, &user).await;
             Ok(())
         }
     }
@@ -1510,10 +1525,22 @@ async fn _delete_cipher_by_uuid(
     if soft_delete {
         cipher.deleted_at = Some(Utc::now().naive_utc());
         cipher.save(conn).await?;
-        nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await).await;
+        nt.send_cipher_update(
+            UpdateType::SyncCipherUpdate,
+            &cipher,
+            &cipher.update_users_revision(conn).await,
+            &headers.device.uuid,
+        )
+        .await;
     } else {
         cipher.delete(conn).await?;
-        nt.send_cipher_update(UpdateType::CipherDelete, &cipher, &cipher.update_users_revision(conn).await).await;
+        nt.send_cipher_update(
+            UpdateType::SyncCipherDelete,
+            &cipher,
+            &cipher.update_users_revision(conn).await,
+            &headers.device.uuid,
+        )
+        .await;
     }
 
     if let Some(org_uuid) = cipher.organization_uuid {
@@ -1575,7 +1602,13 @@ async fn _restore_cipher_by_uuid(
     cipher.deleted_at = None;
     cipher.save(conn).await?;
 
-    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await).await;
+    nt.send_cipher_update(
+        UpdateType::SyncCipherUpdate,
+        &cipher,
+        &cipher.update_users_revision(conn).await,
+        &headers.device.uuid,
+    )
+    .await;
     if let Some(org_uuid) = &cipher.organization_uuid {
         log_event(
             EventType::CipherRestored as i32,
@@ -1652,7 +1685,13 @@ async fn _delete_cipher_attachment_by_id(
 
     // Delete attachment
     attachment.delete(conn).await?;
-    nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(conn).await).await;
+    nt.send_cipher_update(
+        UpdateType::SyncCipherUpdate,
+        &cipher,
+        &cipher.update_users_revision(conn).await,
+        &headers.device.uuid,
+    )
+    .await;
     if let Some(org_uuid) = cipher.organization_uuid {
         log_event(
             EventType::CipherAttachmentDeleted as i32,
diff --git a/src/api/core/folders.rs b/src/api/core/folders.rs
index 95155803..c27d5455 100644
--- a/src/api/core/folders.rs
+++ b/src/api/core/folders.rs
@@ -50,7 +50,7 @@ async fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, mut conn:
     let mut folder = Folder::new(headers.user.uuid, data.Name);
 
     folder.save(&mut conn).await?;
-    nt.send_folder_update(UpdateType::FolderCreate, &folder).await;
+    nt.send_folder_update(UpdateType::SyncFolderCreate, &folder, &headers.device.uuid).await;
 
     Ok(Json(folder.to_json()))
 }
@@ -88,7 +88,7 @@ async fn put_folder(
     folder.name = data.Name;
 
     folder.save(&mut conn).await?;
-    nt.send_folder_update(UpdateType::FolderUpdate, &folder).await;
+    nt.send_folder_update(UpdateType::SyncFolderUpdate, &folder, &headers.device.uuid).await;
 
     Ok(Json(folder.to_json()))
 }
@@ -112,6 +112,6 @@ async fn delete_folder(uuid: String, headers: Headers, mut conn: DbConn, nt: Not
     // Delete the actual folder entry
     folder.delete(&mut conn).await?;
 
-    nt.send_folder_update(UpdateType::FolderDelete, &folder).await;
+    nt.send_folder_update(UpdateType::SyncFolderDelete, &folder, &headers.device.uuid).await;
     Ok(())
 }
diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs
index 885fae81..3393a4d0 100644
--- a/src/api/core/mod.rs
+++ b/src/api/core/mod.rs
@@ -7,8 +7,7 @@ mod organizations;
 mod sends;
 pub mod two_factor;
 
-pub use ciphers::purge_trashed_ciphers;
-pub use ciphers::{CipherSyncData, CipherSyncType};
+pub use ciphers::{purge_trashed_ciphers, CipherSyncData, CipherSyncType};
 pub use emergency_access::{emergency_notification_reminder_job, emergency_request_timeout_job};
 pub use events::{event_cleanup_job, log_event, log_user_event};
 pub use sends::purge_sends;
@@ -47,13 +46,11 @@ pub fn events_routes() -> Vec<Route> {
 //
 // Move this somewhere else
 //
-use rocket::serde::json::Json;
-use rocket::Catcher;
-use rocket::Route;
+use rocket::{serde::json::Json, Catcher, Route};
 use serde_json::Value;
 
 use crate::{
-    api::{JsonResult, JsonUpcase},
+    api::{JsonResult, JsonUpcase, Notify, UpdateType},
     auth::Headers,
     db::DbConn,
     error::Error,
@@ -138,7 +135,12 @@ struct EquivDomainData {
 }
 
 #[post("/settings/domains", data = "<data>")]
-async fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, mut conn: DbConn) -> JsonResult {
+async fn post_eq_domains(
+    data: JsonUpcase<EquivDomainData>,
+    headers: Headers,
+    mut conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
     let data: EquivDomainData = data.into_inner().data;
 
     let excluded_globals = data.ExcludedGlobalEquivalentDomains.unwrap_or_default();
@@ -152,12 +154,19 @@ async fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, mu
 
     user.save(&mut conn).await?;
 
+    nt.send_user_update(UpdateType::SyncSettings, &user).await;
+
     Ok(Json(json!({})))
 }
 
 #[put("/settings/domains", data = "<data>")]
-async fn put_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbConn) -> JsonResult {
-    post_eq_domains(data, headers, conn).await
+async fn put_eq_domains(
+    data: JsonUpcase<EquivDomainData>,
+    headers: Headers,
+    conn: DbConn,
+    nt: Notify<'_>,
+) -> JsonResult {
+    post_eq_domains(data, headers, conn, nt).await
 }
 
 #[get("/hibp/breach?<username>")]
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
index d50a1f12..60d6f714 100644
--- a/src/api/core/organizations.rs
+++ b/src/api/core/organizations.rs
@@ -957,6 +957,7 @@ async fn bulk_confirm_invite(
     headers: AdminHeaders,
     mut conn: DbConn,
     ip: ClientIp,
+    nt: Notify<'_>,
 ) -> Json<Value> {
     let data = data.into_inner().data;
 
@@ -966,7 +967,8 @@ async fn bulk_confirm_invite(
             for invite in keys {
                 let org_user_id = invite["Id"].as_str().unwrap_or_default();
                 let user_key = invite["Key"].as_str().unwrap_or_default();
-                let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &ip).await {
+                let err_msg = match _confirm_invite(&org_id, org_user_id, user_key, &headers, &mut conn, &ip, &nt).await
+                {
                     Ok(_) => String::new(),
                     Err(e) => format!("{:?}", e),
                 };
@@ -998,10 +1000,11 @@ async fn confirm_invite(
     headers: AdminHeaders,
     mut conn: DbConn,
     ip: ClientIp,
+    nt: Notify<'_>,
 ) -> EmptyResult {
     let data = data.into_inner().data;
     let user_key = data["Key"].as_str().unwrap_or_default();
-    _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &ip).await
+    _confirm_invite(&org_id, &org_user_id, user_key, &headers, &mut conn, &ip, &nt).await
 }
 
 async fn _confirm_invite(
@@ -1011,6 +1014,7 @@ async fn _confirm_invite(
     headers: &AdminHeaders,
     conn: &mut DbConn,
     ip: &ClientIp,
+    nt: &Notify<'_>,
 ) -> EmptyResult {
     if key.is_empty() || org_user_id.is_empty() {
         err!("Key or UserId is not set, unable to process request");
@@ -1069,7 +1073,13 @@ async fn _confirm_invite(
         mail::send_invite_confirmed(&address, &org_name).await?;
     }
 
-    user_to_confirm.save(conn).await
+    let save_result = user_to_confirm.save(conn).await;
+
+    if let Some(user) = User::find_by_uuid(&user_to_confirm.user_uuid, conn).await {
+        nt.send_user_update(UpdateType::SyncOrgKeys, &user).await;
+    }
+
+    save_result
 }
 
 #[get("/organizations/<org_id>/users/<org_user_id>")]
@@ -1206,12 +1216,13 @@ async fn bulk_delete_user(
     headers: AdminHeaders,
     mut conn: DbConn,
     ip: ClientIp,
+    nt: Notify<'_>,
 ) -> Json<Value> {
     let data: OrgBulkIds = data.into_inner().data;
 
     let mut bulk_response = Vec::new();
     for org_user_id in data.Ids {
-        let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await {
+        let err_msg = match _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await {
             Ok(_) => String::new(),
             Err(e) => format!("{:?}", e),
         };
@@ -1239,8 +1250,9 @@ async fn delete_user(
     headers: AdminHeaders,
     mut conn: DbConn,
     ip: ClientIp,
+    nt: Notify<'_>,
 ) -> EmptyResult {
-    _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await
+    _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await
 }
 
 #[post("/organizations/<org_id>/users/<org_user_id>/delete")]
@@ -1250,8 +1262,9 @@ async fn post_delete_user(
     headers: AdminHeaders,
     mut conn: DbConn,
     ip: ClientIp,
+    nt: Notify<'_>,
 ) -> EmptyResult {
-    _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip).await
+    _delete_user(&org_id, &org_user_id, &headers, &mut conn, &ip, &nt).await
 }
 
 async fn _delete_user(
@@ -1260,6 +1273,7 @@ async fn _delete_user(
     headers: &AdminHeaders,
     conn: &mut DbConn,
     ip: &ClientIp,
+    nt: &Notify<'_>,
 ) -> EmptyResult {
     let user_to_delete = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await {
         Some(user) => user,
@@ -1288,6 +1302,10 @@ async fn _delete_user(
     )
     .await;
 
+    if let Some(user) = User::find_by_uuid(&user_to_delete.user_uuid, conn).await {
+        nt.send_user_update(UpdateType::SyncOrgKeys, &user).await;
+    }
+
     user_to_delete.delete(conn).await
 }
 
diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs
index e2107256..b7b2a0ce 100644
--- a/src/api/core/sends.rs
+++ b/src/api/core/sends.rs
@@ -381,6 +381,7 @@ async fn post_access(
     data: JsonUpcase<SendAccessData>,
     mut conn: DbConn,
     ip: ClientIp,
+    nt: Notify<'_>,
 ) -> JsonResult {
     let mut send = match Send::find_by_access_id(&access_id, &mut conn).await {
         Some(s) => s,
@@ -422,6 +423,8 @@ async fn post_access(
 
     send.save(&mut conn).await?;
 
+    nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await).await;
+
     Ok(Json(send.to_json_access(&mut conn).await))
 }
 
@@ -432,6 +435,7 @@ async fn post_access_file(
     data: JsonUpcase<SendAccessData>,
     host: Host,
     mut conn: DbConn,
+    nt: Notify<'_>,
 ) -> JsonResult {
     let mut send = match Send::find_by_uuid(&send_id, &mut conn).await {
         Some(s) => s,
@@ -470,6 +474,8 @@ async fn post_access_file(
 
     send.save(&mut conn).await?;
 
+    nt.send_send_update(UpdateType::SyncSendUpdate, &send, &send.update_users_revision(&mut conn).await).await;
+
     let token_claims = crate::auth::generate_send_claims(&send_id, &file_id);
     let token = crate::auth::encode_jwt(&token_claims);
     Ok(Json(json!({
diff --git a/src/api/notifications.rs b/src/api/notifications.rs
index cd53c96d..b51e1380 100644
--- a/src/api/notifications.rs
+++ b/src/api/notifications.rs
@@ -164,12 +164,13 @@ impl WebSocketUsers {
         let data = create_update(
             vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))],
             ut,
+            None,
         );
 
         self.send_update(&user.uuid, &data).await;
     }
 
-    pub async fn send_folder_update(&self, ut: UpdateType, folder: &Folder) {
+    pub async fn send_folder_update(&self, ut: UpdateType, folder: &Folder, acting_device_uuid: &String) {
         let data = create_update(
             vec![
                 ("Id".into(), folder.uuid.clone().into()),
@@ -177,12 +178,19 @@ impl WebSocketUsers {
                 ("RevisionDate".into(), serialize_date(folder.updated_at)),
             ],
             ut,
+            Some(acting_device_uuid.into()),
         );
 
         self.send_update(&folder.user_uuid, &data).await;
     }
 
-    pub async fn send_cipher_update(&self, ut: UpdateType, cipher: &Cipher, user_uuids: &[String]) {
+    pub async fn send_cipher_update(
+        &self,
+        ut: UpdateType,
+        cipher: &Cipher,
+        user_uuids: &[String],
+        acting_device_uuid: &String,
+    ) {
         let user_uuid = convert_option(cipher.user_uuid.clone());
         let org_uuid = convert_option(cipher.organization_uuid.clone());
 
@@ -195,6 +203,7 @@ impl WebSocketUsers {
                 ("RevisionDate".into(), serialize_date(cipher.updated_at)),
             ],
             ut,
+            Some(acting_device_uuid.into()),
         );
 
         for uuid in user_uuids {
@@ -212,6 +221,7 @@ impl WebSocketUsers {
                 ("RevisionDate".into(), serialize_date(send.revision_date)),
             ],
             ut,
+            None,
         );
 
         for uuid in user_uuids {
@@ -228,14 +238,14 @@ impl WebSocketUsers {
     "ReceiveMessage", // Target
     [ // Arguments
         {
-            "ContextId": "app_id",
+            "ContextId": acting_device_uuid || Nil,
             "Type": ut as i32,
             "Payload": {}
         }
     ]
 ]
 */
-fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType) -> Vec<u8> {
+fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType, acting_device_uuid: Option<String>) -> Vec<u8> {
     use rmpv::Value as V;
 
     let value = V::Array(vec![
@@ -244,7 +254,7 @@ fn create_update(payload: Vec<(Value, Value)>, ut: UpdateType) -> Vec<u8> {
         V::Nil,
         "ReceiveMessage".into(),
         V::Array(vec![V::Map(vec![
-            ("ContextId".into(), "app_id".into()),
+            ("ContextId".into(), acting_device_uuid.map(|v| v.into()).unwrap_or_else(|| V::Nil)),
             ("Type".into(), (ut as i32).into()),
             ("Payload".into(), payload.into()),
         ])]),
@@ -260,17 +270,17 @@ fn create_ping() -> Vec<u8> {
 #[allow(dead_code)]
 #[derive(Eq, PartialEq)]
 pub enum UpdateType {
-    CipherUpdate = 0,
-    CipherCreate = 1,
-    LoginDelete = 2,
-    FolderDelete = 3,
-    Ciphers = 4,
+    SyncCipherUpdate = 0,
+    SyncCipherCreate = 1,
+    SyncLoginDelete = 2,
+    SyncFolderDelete = 3,
+    SyncCiphers = 4,
 
-    Vault = 5,
-    OrgKeys = 6,
-    FolderCreate = 7,
-    FolderUpdate = 8,
-    CipherDelete = 9,
+    SyncVault = 5,
+    SyncOrgKeys = 6,
+    SyncFolderCreate = 7,
+    SyncFolderUpdate = 8,
+    SyncCipherDelete = 9,
     SyncSettings = 10,
 
     LogOut = 11,
@@ -279,6 +289,9 @@ pub enum UpdateType {
     SyncSendUpdate = 13,
     SyncSendDelete = 14,
 
+    AuthRequest = 15,
+    AuthRequestResponse = 16,
+
     None = 100,
 }