diff --git a/src/api/admin.rs b/src/api/admin.rs
index 54cf0eb00af9e4c147ba80e4f476aeaaba62688f..ba7be5f961f47f8b30cbaa31e2077dc413027818 100644
--- a/src/api/admin.rs
+++ b/src/api/admin.rs
@@ -36,7 +36,17 @@ fn invite_user(data: JsonUpcase<InviteData>, _token: AdminToken, conn: DbConn) -
         err!("User already exists")
     }
 
-    err!("Unimplemented")
+    if !CONFIG.invitations_allowed {
+        err!("Invitations are not allowed")
+    }
+
+    let mut invitation = Invitation::new(data.Email.clone());
+    let mut user = User::new(data.Email);
+
+    invitation.save(&conn)?;
+    user.save(&conn)?;
+
+    Ok(Json(json!({})))
 }
 
 #[post("/users/<uuid>/delete")]
@@ -46,10 +56,8 @@ fn delete_user(uuid: String, _token: AdminToken, conn: DbConn) -> JsonResult {
         None => err!("User doesn't exist"),
     };
 
-    match user.delete(&conn) {
-        Ok(_) => Ok(Json(json!({}))),
-        Err(e) => err!("Error deleting user", e),
-    }
+    user.delete(&conn)?;
+    Ok(Json(json!({})))
 }
 
 pub struct AdminToken {}
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index a8132a7fe9c8d35f0956620719ed3f43e8bc5f08..fe99a9c415dfcd20e38960768e1940ad6d3e50d7 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -65,9 +65,7 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
                 if CONFIG.mail.is_none() {
                     for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).iter_mut() {
                         user_org.status = UserOrgStatus::Accepted as i32;
-                        if user_org.save(&conn).is_err() {
-                            err!("Failed to accept user to organization")
-                        }
+                        user_org.save(&conn)?;
                     }
                     if !Invitation::take(&data.Email, &conn) {
                         err!("Error accepting invitation")
@@ -128,10 +126,7 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
         user.public_key = Some(keys.PublicKey);
     }
 
-    match user.save(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to save user"),
-    }
+    user.save(&conn)
 }
 
 #[get("/accounts/profile")]
@@ -164,10 +159,8 @@ fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -
         Some(ref h) if h.is_empty() => None,
         _ => data.MasterPasswordHint,
     };
-    match user.save(&conn) {
-        Ok(()) => Ok(Json(user.to_json(&conn))),
-        Err(_) => err!("Failed to save user profile"),
-    }
+    user.save(&conn)?;
+    Ok(Json(user.to_json(&conn)))
 }
 
 #[get("/users/<uuid>/public-key")]
@@ -193,10 +186,8 @@ fn post_keys(data: JsonUpcase<KeysData>, headers: Headers, conn: DbConn) -> Json
     user.private_key = Some(data.EncryptedPrivateKey);
     user.public_key = Some(data.PublicKey);
 
-    match user.save(&conn) {
-        Ok(()) => Ok(Json(user.to_json(&conn))),
-        Err(_) => err!("Failed to save the user's keys"),
-    }
+    user.save(&conn)?;
+    Ok(Json(user.to_json(&conn)))
 }
 
 #[derive(Deserialize)]
@@ -218,10 +209,7 @@ fn post_password(data: JsonUpcase<ChangePassData>, headers: Headers, conn: DbCon
 
     user.set_password(&data.NewMasterPasswordHash);
     user.key = data.Key;
-    match user.save(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to save password"),
-    }
+    user.save(&conn)
 }
 
 #[derive(Deserialize)]
@@ -248,10 +236,7 @@ fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, conn: DbConn) ->
     user.client_kdf_type = data.Kdf;
     user.set_password(&data.NewMasterPasswordHash);
     user.key = data.Key;
-    match user.save(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to save password settings"),
-    }
+    user.save(&conn)
 }
 
 #[derive(Deserialize)]
@@ -295,9 +280,7 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, ws:
         }
 
         saved_folder.name = folder_data.Name;
-        if saved_folder.save(&conn).is_err() {
-            err!("Failed to save folder")
-        }
+        saved_folder.save(&conn)?
     }
 
     // Update cipher data
@@ -323,11 +306,7 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, ws:
     user.private_key = Some(data.PrivateKey);
     user.reset_security_stamp();
 
-    if user.save(&conn).is_err() {
-        err!("Failed modify user key");
-    }
-
-    Ok(())
+    user.save(&conn)
 }
 
 #[post("/accounts/security-stamp", data = "<data>")]
@@ -340,10 +319,7 @@ fn post_sstamp(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -
     }
 
     user.reset_security_stamp();
-    match user.save(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to reset security stamp"),
-    }
+    user.save(&conn)
 }
 
 #[derive(Deserialize)]
@@ -398,10 +374,7 @@ fn post_email(data: JsonUpcase<ChangeEmailData>, headers: Headers, conn: DbConn)
     user.set_password(&data.NewMasterPasswordHash);
     user.key = data.Key;
 
-    match user.save(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to save email address"),
-    }
+    user.save(&conn)
 }
 
 #[post("/accounts/delete", data = "<data>")]
@@ -418,10 +391,7 @@ fn delete_account(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn
         err!("Invalid password")
     }
 
-    match user.delete(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed deleting user account, are you the only owner of some organization?"),
-    }
+    user.delete(&conn)
 }
 
 #[get("/accounts/revision-date")]
@@ -446,9 +416,7 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
     };
 
     if let Some(ref mail_config) = CONFIG.mail {
-        if let Err(e) = mail::send_password_hint(&data.Email, hint, mail_config) {
-            err!(format!("There have been a problem sending the email: {}", e));
-        }
+        mail::send_password_hint(&data.Email, hint, mail_config)?;
     } else if CONFIG.show_password_hint {
         if let Some(hint) = hint {
             err!(format!("Your password hint is: {}", &hint));
diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs
index f1d3f38ae0653333c3e26932e69fa0f5961bc737..c3edf2e1ff9f6a6ee05ce9f0ceb8500e08029ff5 100644
--- a/src/api/core/ciphers.rs
+++ b/src/api/core/ciphers.rs
@@ -182,10 +182,7 @@ fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn:
 
     let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
     cipher.user_uuid = Some(headers.user.uuid.clone());
-    match cipher.save(&conn) {
-        Ok(()) => (),
-        Err(_) => err!("Failed saving cipher")
-    };
+    cipher.save(&conn)?;
 
     share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &ws)
 }
@@ -248,10 +245,7 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
             saved_att.key = Some(attachment.Key);
             saved_att.file_name = attachment.FileName;
 
-            match saved_att.save(&conn) {
-                Ok(()) => (),
-                Err(_) => err!("Failed to save attachment")
-            };
+            saved_att.save(&conn)?;
         }
     }
 
@@ -284,17 +278,11 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
     cipher.data = type_data.to_string();
     cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
 
-    match cipher.save(&conn) {
-        Ok(()) => (),
-        Err(_) => err!("Failed to save cipher")
-    };
-    ws.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn));
+    cipher.save(&conn)?;
 
-    if cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn).is_err() {
-        err!("Error saving the folder information")
-    }
+    ws.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn));
 
-    Ok(())
+    cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn)
 }
 
 use super::folders::FolderData;
@@ -325,11 +313,9 @@ fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbC
     let mut folders: Vec<_> = Vec::new();
     for folder in data.Folders.into_iter() {
         let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.Name);
-        if new_folder.save(&conn).is_err() {
-            err!("Failed importing folders")
-        } else {
-            folders.push(new_folder);
-        }
+        new_folder.save(&conn)?;
+
+        folders.push(new_folder);
     }
 
     // Read the relations between folders and ciphers
@@ -351,10 +337,7 @@ fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbC
     }
 
     let mut user = headers.user;
-    match user.update_revision(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to update the revision, please log out and log back in to finish import.")
-    }
+    user.update_revision(&conn)
 }
 
 
@@ -429,15 +412,9 @@ fn post_collections_admin(uuid: String, data: JsonUpcase<CollectionsAdminData>,
             Some(collection) => {
                 if collection.is_writable_by_user(&headers.user.uuid, &conn) {
                     if posted_collections.contains(&collection.uuid) { // Add to collection
-                        match CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn) {
-                            Ok(()) => (),
-                            Err(_) => err!("Failed to add cipher to collection")
-                        };
+                        CollectionCipher::save(&cipher.uuid, &collection.uuid, &conn)?;
                     } else { // Remove from collection
-                        match CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn) {
-                            Ok(()) => (),
-                            Err(_) => err!("Failed to remove cipher from collection")
-                        };
+                        CollectionCipher::delete(&cipher.uuid, &collection.uuid, &conn)?;
                     }
                 } else {
                     err!("No rights to modify the collection")
@@ -540,10 +517,7 @@ fn share_cipher_by_uuid(uuid: &str, data: ShareCipherData, headers: &Headers, co
                     None => err!("Invalid collection ID provided"),
                     Some(collection) => {
                         if collection.is_writable_by_user(&headers.user.uuid, &conn) {
-                            match CollectionCipher::save(&cipher.uuid.clone(), &collection.uuid, &conn) {
-                                Ok(()) => (),
-                                Err(_) => err!("Failed to add cipher to collection")
-                            };
+                            CollectionCipher::save(&cipher.uuid.clone(), &collection.uuid, &conn)?;
                             shared_to_collection = true;
                         } else {
                             err!("No rights to modify the collection")
@@ -614,10 +588,7 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
 
                 let mut attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size);
                 attachment.key = attachment_key.clone();
-                match attachment.save(&conn) {
-                    Ok(()) => (),
-                    Err(_) => error!("Failed to save attachment")
-                };
+                attachment.save(&conn).expect("Error saving attachment");
             },
             _ => error!("Invalid multipart name")
         }
@@ -746,13 +717,9 @@ fn move_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn,
         }
 
         // Move cipher
-        if cipher.move_to_folder(folder_id.clone(), &headers.user.uuid, &conn).is_err() {
-            err!("Error saving the folder information")
-        }
-        match cipher.save(&conn) {
-            Ok(()) => (),
-            Err(_) => err!("Failed to save cipher")
-        };
+        cipher.move_to_folder(folder_id.clone(), &headers.user.uuid, &conn)?;
+        cipher.save(&conn)?;
+
         ws.send_cipher_update(UpdateType::SyncCipherUpdate, &cipher, &cipher.update_users_revision(&conn));
     }
 
@@ -777,21 +744,14 @@ fn delete_all(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn, ws
 
     // Delete ciphers and their attachments
     for cipher in Cipher::find_owned_by_user(&user.uuid, &conn) {
-        if cipher.delete(&conn).is_err() {
-            err!("Failed deleting cipher")
-        }
-        else {
-            ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
-        }
+        cipher.delete(&conn)?;
+        ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
     }
 
     // Delete folders
     for f in Folder::find_by_user(&user.uuid, &conn) {
-        if f.delete(&conn).is_err() {
-            err!("Failed deleting folder")
-        } else {
-            ws.send_folder_update(UpdateType::SyncFolderCreate, &f);
-        }
+        f.delete(&conn)?;
+        ws.send_folder_update(UpdateType::SyncFolderCreate, &f);
     }
 
     Ok(())
@@ -807,13 +767,9 @@ fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, ws: &Sta
         err!("Cipher can't be deleted by user")
     }
 
-    match cipher.delete(&conn) {
-        Ok(()) => {
-            ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
-            Ok(())
-        }
-        Err(_) => err!("Failed deleting cipher")
-    }
+    cipher.delete(&conn)?;
+    ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
+    Ok(())
 }
 
 fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &Headers, conn: &DbConn, ws: &State<WebSocketUsers>) -> EmptyResult {
@@ -836,11 +792,7 @@ fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &He
     }
 
     // Delete attachment
-    match attachment.delete(&conn) {
-        Ok(()) => {
-            ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
-            Ok(())
-        }
-        Err(_) => err!("Deleting attachment failed")
-    }
+    attachment.delete(&conn)?;
+    ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
+    Ok(())
 }
diff --git a/src/api/core/folders.rs b/src/api/core/folders.rs
index 4c1cdff689f0ee0a0bcc2d65067ba5f3fb1547e5..4585d530b413a10881d91ce47c3104a90c891789 100644
--- a/src/api/core/folders.rs
+++ b/src/api/core/folders.rs
@@ -62,9 +62,7 @@ fn post_folders(data: JsonUpcase<FolderData>, headers: Headers, conn: DbConn, ws
 
     let mut folder = Folder::new(headers.user.uuid.clone(), data.Name);
 
-    if folder.save(&conn).is_err() {
-        err!("Failed to save folder")
-    }
+    folder.save(&conn)?;
     ws.send_folder_update(UpdateType::SyncFolderCreate, &folder);
 
     Ok(Json(folder.to_json()))
@@ -90,9 +88,7 @@ fn put_folder(uuid: String, data: JsonUpcase<FolderData>, headers: Headers, conn
 
     folder.name = data.Name;
 
-    if folder.save(&conn).is_err() {
-        err!("Failed to save folder")
-    }
+    folder.save(&conn)?;
     ws.send_folder_update(UpdateType::SyncFolderUpdate, &folder);
 
     Ok(Json(folder.to_json()))
@@ -115,11 +111,8 @@ fn delete_folder(uuid: String, headers: Headers, conn: DbConn, ws: State<WebSock
     }
 
     // Delete the actual folder entry
-    match folder.delete(&conn) {
-        Ok(()) => {
-            ws.send_folder_update(UpdateType::SyncFolderDelete, &folder);
-            Ok(())
-        }
-        Err(_) => err!("Failed deleting folder")
-    }
+    folder.delete(&conn)?;
+
+    ws.send_folder_update(UpdateType::SyncFolderDelete, &folder);
+    Ok(())
 }
diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs
index ef7c254d21c053e64c4021c8daa67557bfb4363e..46c3fbfadb354fb6d975dfb48811688dcd1586db 100644
--- a/src/api/core/mod.rs
+++ b/src/api/core/mod.rs
@@ -120,10 +120,9 @@ fn post_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: Db
     user.excluded_globals = to_string(&excluded_globals).unwrap_or("[]".to_string());
     user.equivalent_domains = to_string(&equivalent_domains).unwrap_or("[]".to_string());
 
-    match user.save(&conn) {
-        Ok(()) => Ok(Json(json!({}))),
-        Err(_) => err!("Failed to save user"),
-    }
+    user.save(&conn)?;
+
+    Ok(Json(json!({})))
 }
 
 #[put("/settings/domains", data = "<data>")]
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
index b3350fb991870cff495609baa8a3e1cf5864da88..5d326dc062bfa273e50ddeda863cab2bf4c9e0ee 100644
--- a/src/api/core/organizations.rs
+++ b/src/api/core/organizations.rs
@@ -93,16 +93,9 @@ fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn
     user_org.type_ = UserOrgType::Owner as i32;
     user_org.status = UserOrgStatus::Confirmed as i32;
 
-    if org.save(&conn).is_err() {
-        err!("Failed creating organization")
-    }
-    if user_org.save(&conn).is_err() {
-        err!("Failed to add user to organization")
-    }
-
-    if collection.save(&conn).is_err() {
-        err!("Failed creating Collection");
-    }
+    org.save(&conn)?;
+    user_org.save(&conn)?;
+    collection.save(&conn)?;
 
     Ok(Json(org.to_json()))
 }
@@ -118,10 +111,7 @@ fn delete_organization(org_id: String, data: JsonUpcase<PasswordData>, headers:
 
     match Organization::find_by_uuid(&org_id, &conn) {
         None => err!("Organization not found"),
-        Some(org) => match org.delete(&conn) {
-            Ok(()) => Ok(()),
-            Err(_) => err!("Failed deleting the organization")
-        }
+        Some(org) => org.delete(&conn)
     }
 }
 
@@ -145,10 +135,7 @@ fn leave_organization(org_id: String, headers: Headers, conn: DbConn) -> EmptyRe
                 }
             }
             
-            match user_org.delete(&conn) {
-                Ok(()) => Ok(()),
-                Err(_) => err!("Failed leaving the organization")
-            }
+            user_org.delete(&conn)
         }
     }
 }
@@ -178,10 +165,8 @@ fn post_organization(org_id: String, _headers: OwnerHeaders, data: JsonUpcase<Or
     org.name = data.Name;
     org.billing_email = data.BillingEmail;
 
-    match org.save(&conn) {
-        Ok(()) => Ok(Json(org.to_json())),
-        Err(_) => err!("Failed to modify organization")
-    }
+    org.save(&conn)?;
+    Ok(Json(org.to_json()))
 }
 
 // GET /api/collections?writeOnly=false
@@ -222,10 +207,7 @@ fn post_organization_collections(org_id: String, _headers: AdminHeaders, data: J
     };
 
     let mut collection = Collection::new(org.uuid.clone(), data.Name);
-
-    if collection.save(&conn).is_err() {
-        err!("Failed saving Collection");
-    }
+    collection.save(&conn)?;
 
     Ok(Json(collection.to_json()))
 }
@@ -254,9 +236,7 @@ fn post_organization_collection_update(org_id: String, col_id: String, _headers:
     }
 
     collection.name = data.Name.clone();
-    if collection.save(&conn).is_err() {
-        err!("Failed updating Collection");
-    }
+    collection.save(&conn)?;
 
     Ok(Json(collection.to_json()))
 }
@@ -279,10 +259,7 @@ fn delete_organization_collection_user(org_id: String, col_id: String, org_user_
             match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &conn) {
                 None => err!("User not assigned to collection"),
                 Some(col_user) => {
-                    match col_user.delete(&conn) {
-                        Ok(()) => Ok(()),
-                        Err(_) => err!("Failed removing user from collection")
-                    }
+                    col_user.delete(&conn)
                 }
             }
         }
@@ -299,10 +276,7 @@ fn delete_organization_collection(org_id: String, col_id: String, _headers: Admi
     match Collection::find_by_uuid(&col_id, &conn) {
         None => err!("Collection not found"),
         Some(collection) => if collection.org_uuid == org_id {
-            match collection.delete(&conn) {
-                Ok(()) => Ok(()),
-                Err(_) => err!("Failed deleting collection")
-            }
+            collection.delete(&conn)
         } else {
             err!("Collection and Organization id do not match")
         }
@@ -435,18 +409,11 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
         let user = match User::find_by_mail(&email, &conn) {
             None => if CONFIG.invitations_allowed { // Invite user if that's enabled
                 let mut invitation = Invitation::new(email.clone());
-                match invitation.save(&conn) {
-                    Ok(()) => {
-                        let mut user = User::new(email.clone());
-                        if user.save(&conn).is_err() {
-                            err!("Failed to create placeholder for invited user")
-                        } else {
-                            user_org_status = UserOrgStatus::Invited as i32;
-                            user
-                        }
-                    }
-                    Err(_) => err!(format!("Failed to invite: {}", email))
-                }
+                invitation.save(&conn)?;
+                let mut user = User::new(email.clone());
+                user.save(&conn)?;
+                user_org_status = UserOrgStatus::Invited as i32;
+                user
                 
             } else {
                 err!(format!("User email does not exist: {}", email))
@@ -474,17 +441,13 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
                     match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
                         None => err!("Collection not found in Organization"),
                         Some(collection) => {
-                            if CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, &conn).is_err() {
-                                err!("Failed saving collection access for user")
-                            }
+                            CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, &conn)?;
                         }
                     }
                 }
             }
 
-            if new_user.save(&conn).is_err() {
-                err!("Failed to add user to organization")
-            }
+            new_user.save(&conn)?;
             org_user_id = Some(new_user.uuid.clone());
         }
 
@@ -627,10 +590,7 @@ fn confirm_invite(org_id: String, org_user_id: String, data: JsonUpcase<Value>,
         None => err!("Invalid key provided")
     };
 
-    match user_to_confirm.save(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to add user to organization")
-    }
+    user_to_confirm.save(&conn)
 }
 
 #[get("/organizations/<org_id>/users/<org_user_id>")]
@@ -702,9 +662,7 @@ fn edit_user(org_id: String, org_user_id: String, data: JsonUpcase<EditUserData>
 
     // Delete all the odd collections
     for c in CollectionUser::find_by_organization_and_user_uuid(&org_id, &user_to_edit.user_uuid, &conn) {
-        if c.delete(&conn).is_err() {
-            err!("Failed deleting old collection assignment")
-        }
+        c.delete(&conn)?;
     }
 
     // If no accessAll, add the collections received
@@ -713,18 +671,13 @@ fn edit_user(org_id: String, org_user_id: String, data: JsonUpcase<EditUserData>
             match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
                 None => err!("Collection not found in Organization"),
                 Some(collection) => {
-                    if CollectionUser::save(&user_to_edit.user_uuid, &collection.uuid, col.ReadOnly, &conn).is_err() {
-                        err!("Failed saving collection access for user")
-                    }
+                    CollectionUser::save(&user_to_edit.user_uuid, &collection.uuid, col.ReadOnly, &conn)?;
                 }
             }
         }
     }
 
-    match user_to_edit.save(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to save user data")
-    }
+    user_to_edit.save(&conn)
 }
 
 #[delete("/organizations/<org_id>/users/<org_user_id>")]
@@ -736,10 +689,7 @@ fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn:
                 if user_to_delete.uuid == headers.user.uuid {
                     err!("Delete your account in the account settings")
                 } else {
-                    match user_to_delete.delete(&conn) {
-                        Ok(()) => return Ok(()),
-                        Err(_) => err!("Failed to delete user - likely because it's the only owner of organization")
-                    }
+                    user_to_delete.delete(&conn)?;
                 }
             },
             None => err!("User not found")
@@ -767,10 +717,7 @@ fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn:
         }
     }
 
-    match user_to_delete.delete(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed deleting user from organization")
-    }
+    user_to_delete.delete(&conn)
 }
 
 #[post("/organizations/<org_id>/users/<org_user_id>/delete")]
@@ -844,15 +791,9 @@ fn post_org_import(query: Form<OrgIdData>, data: JsonUpcase<ImportData>, headers
             Err(_) => err!("Failed to assign to collection")
         };
         
-        match CollectionCipher::save(cipher_id, coll_id, &conn) {
-            Ok(()) => (),
-            Err(_) => err!("Failed to add cipher to collection")
-        };
+        CollectionCipher::save(cipher_id, coll_id, &conn)?;
     }
 
     let mut user = headers.user;
-    match user.update_revision(&conn) {
-        Ok(()) => Ok(()),
-        Err(_) => err!("Failed to update the revision, please log out and log back in to finish import.")
-    }
+    user.update_revision(&conn)
 }
diff --git a/src/api/core/two_factor.rs b/src/api/core/two_factor.rs
index ef017af7b3a3a0b9d119d0bcba3d783bb3fdf46a..264510966657f553a7f3d00fa8e09682a2869d36 100644
--- a/src/api/core/two_factor.rs
+++ b/src/api/core/two_factor.rs
@@ -11,7 +11,7 @@ use crate::db::{
 
 use crate::crypto;
 
-use crate::api::{ApiResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
+use crate::api::{ApiResult, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
 use crate::auth::Headers;
 
 use rocket::Route;
@@ -99,10 +99,8 @@ fn recover(data: JsonUpcase<RecoverTwoFactor>, conn: DbConn) -> JsonResult {
 
     // Remove the recovery code, not needed without twofactors
     user.totp_recover = None;
-    match user.save(&conn) {
-        Ok(()) => Ok(Json(json!({}))),
-        Err(_) => err!("Failed to remove the user's two factor recovery code")
-    }
+    user.save(&conn)?;
+    Ok(Json(json!({})))
 }
 
 #[derive(Deserialize)]
@@ -242,9 +240,7 @@ fn _generate_recover_code(user: &mut User, conn: &DbConn) {
     if user.totp_recover.is_none() {
         let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20]));
         user.totp_recover = Some(totp_recover);
-        if user.save(conn).is_err() {
-            error!("Failed to save the user's two factor recovery code")
-        }
+        user.save(conn).ok();
     }
 }
 
@@ -349,15 +345,11 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
     );
 
     if let Some(tf_challenge) = tf_challenge {
-        let challenge: Challenge = serde_json::from_str(&tf_challenge.data)
-            .expect("Can't parse U2fRegisterChallenge data");
+        let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
 
-        tf_challenge
-            .delete(&conn)
-            .expect("Error deleting U2F register challenge");
+        tf_challenge.delete(&conn)?;
 
-        let response_copy: RegisterResponseCopy =
-            serde_json::from_str(&data.DeviceResponse).expect("Can't parse RegisterResponse data");
+        let response_copy: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
 
         let error_code = response_copy
             .error_code
@@ -370,40 +362,31 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
 
         let response = response_copy.into_response(challenge.challenge.clone());
 
-        match U2F.register_response(challenge.clone(), response) {
-            Ok(registration) => {
-                // TODO: Allow more than one U2F device
-                let mut registrations = Vec::new();
-                registrations.push(registration);
-
-                let tf_registration = TwoFactor::new(
-                    headers.user.uuid.clone(),
-                    TwoFactorType::U2f,
-                    serde_json::to_string(&registrations).unwrap(),
-                );
-                tf_registration
-                    .save(&conn)
-                    .expect("Error saving U2F registration");
-
-                let mut user = headers.user;
-                _generate_recover_code(&mut user, &conn);
-
-                Ok(Json(json!({
-                    "Enabled": true,
-                    "Challenge": {
-                        "UserId": user.uuid,
-                        "AppId": APP_ID.to_string(),
-                        "Challenge": challenge,
-                        "Version": U2F_VERSION,
-                    },
-                    "Object": "twoFactorU2f"
-                })))
-            }
-            Err(e) => {
-                error!("{:#?}", e);
-                err!("Error activating u2f")
-            }
-        }
+        let registration = U2F.register_response(challenge.clone(), response)?;
+        // TODO: Allow more than one U2F device
+        let mut registrations = Vec::new();
+        registrations.push(registration);
+
+        let tf_registration = TwoFactor::new(
+            headers.user.uuid.clone(),
+            TwoFactorType::U2f,
+            serde_json::to_string(&registrations).unwrap(),
+        );
+        tf_registration.save(&conn)?;
+
+        let mut user = headers.user;
+        _generate_recover_code(&mut user, &conn);
+
+        Ok(Json(json!({
+            "Enabled": true,
+            "Challenge": {
+                "UserId": user.uuid,
+                "AppId": APP_ID.to_string(),
+                "Challenge": challenge,
+                "Version": U2F_VERSION,
+            },
+            "Object": "twoFactorU2f"
+        })))
     } else {
         err!("Can't recover challenge")
     }
@@ -469,7 +452,7 @@ pub fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRe
     Ok(signed_request)
 }
 
-pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> ApiResult<()> {
+pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
     let challenge_type = TwoFactorType::U2fLoginChallenge as i32;
     let u2f_type = TwoFactorType::U2f as i32;
 
@@ -477,11 +460,8 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Api
 
     let challenge = match tf_challenge {
         Some(tf_challenge) => {
-            let challenge: Challenge = serde_json::from_str(&tf_challenge.data)
-                .expect("Can't parse U2fLoginChallenge data");
-            tf_challenge
-                .delete(&conn)
-                .expect("Error deleting U2F login challenge");
+            let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
+            tf_challenge.delete(&conn)?;
             challenge
         }
         None => err!("Can't recover login challenge"),
@@ -494,8 +474,7 @@ pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> Api
 
     let registrations = _parse_registrations(&twofactor.data);
 
-    let response: SignResponse =
-        serde_json::from_str(response).expect("Can't parse SignResponse data");
+    let response: SignResponse = serde_json::from_str(response)?;
 
     let mut _counter: u32 = 0;
     for registration in registrations {
@@ -614,8 +593,7 @@ fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbCo
     let r = TwoFactor::find_by_user_and_type(user_uuid, yubikey_type, &conn);
 
     if let Some(r) = r {
-        let yubikey_metadata: YubikeyMetadata =
-            serde_json::from_str(&r.data).expect("Can't parse YubikeyMetadata data");
+        let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;
 
         let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
 
@@ -648,7 +626,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
     );
 
     if let Some(yubikey_data) = yubikey_data {
-        yubikey_data.delete(&conn).expect("Error deleting current Yubikeys");
+        yubikey_data.delete(&conn)?;
     }
 
     let yubikeys = parse_yubikeys(&data);
@@ -686,8 +664,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
         TwoFactorType::YubiKey,
         serde_json::to_string(&yubikey_metadata).unwrap(),
     );
-    yubikey_registration
-        .save(&conn).expect("Failed to save Yubikey info");
+    yubikey_registration.save(&conn)?;
 
     let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
 
@@ -703,7 +680,7 @@ fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, c
     activate_yubikey(data, headers, conn)
 }
 
-pub fn validate_yubikey_login(user_uuid: &str, response: &str, conn: &DbConn) -> ApiResult<()> {
+pub fn validate_yubikey_login(user_uuid: &str, response: &str, conn: &DbConn) -> EmptyResult {
     if response.len() != 44 {
         err!("Invalid Yubikey OTP length");
     }
diff --git a/src/api/identity.rs b/src/api/identity.rs
index 028961291c94eabd57ba4feb445c4b304160a763..e0efc029362342f2d9287c8bbcd74aa6ba4a74a2 100644
--- a/src/api/identity.rs
+++ b/src/api/identity.rs
@@ -61,17 +61,16 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
     let orgs = UserOrganization::find_by_user(&user.uuid, &conn);
 
     let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
-    match device.save(&conn) {
-        Ok(()) => Ok(Json(json!({
-            "access_token": access_token,
-            "expires_in": expires_in,
-            "token_type": "Bearer",
-            "refresh_token": device.refresh_token,
-            "Key": user.key,
-            "PrivateKey": user.private_key,
-        }))),
-        Err(e) => err!("Failed to add device to user", e),
-    }
+
+    device.save(&conn)?;
+    Ok(Json(json!({
+        "access_token": access_token,
+        "expires_in": expires_in,
+        "token_type": "Bearer",
+        "refresh_token": device.refresh_token,
+        "Key": user.key,
+        "PrivateKey": user.private_key,
+    })))
 }
 
 fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult {
@@ -85,19 +84,19 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult
     let username = data.username.as_ref().unwrap();
     let user = match User::find_by_mail(username, &conn) {
         Some(user) => user,
-        None => err!(format!(
-            "Username or password is incorrect. Try again. IP: {}. Username: {}.",
-            ip.ip, username
-        )),
+        None => err!(
+            "Username or password is incorrect. Try again",
+            format!("IP: {}. Username: {}.", ip.ip, username)
+        ),
     };
 
     // Check password
     let password = data.password.as_ref().unwrap();
     if !user.check_valid_password(password) {
-        err!(format!(
-            "Username or password is incorrect. Try again. IP: {}. Username: {}.",
-            ip.ip, username
-        ))
+        err!(
+            "Username or password is incorrect. Try again",
+            format!("IP: {}. Username: {}.", ip.ip, username)
+        )
     }
 
     // On iOS, device_type sends "iOS", on others it sends a number
@@ -126,9 +125,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult
     let orgs = UserOrganization::find_by_user(&user.uuid, &conn);
 
     let (access_token, expires_in) = device.refresh_tokens(&user, orgs);
-    if let Err(e) = device.save(&conn) {
-        err!("Failed to add device to user", e)
-    }
+    device.save(&conn)?;
 
     let mut result = json!({
         "access_token": access_token,
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 3bb2605df276138db4340680febcd877fd993e5f..c719d15e9d83cd356fd87c2ecb770686d709e4c0 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -13,14 +13,13 @@ pub use self::web::routes as web_routes;
 pub use self::notifications::routes as notifications_routes;
 pub use self::notifications::{start_notification_server, WebSocketUsers, UpdateType};
 
-use rocket::response::status::BadRequest;
 use rocket_contrib::json::Json;
 use serde_json::Value;
 
 // Type aliases for API methods results
-type ApiResult<T> = Result<T, BadRequest<Json<Value>>>;
-type JsonResult = ApiResult<Json<Value>>;
-type EmptyResult = ApiResult<()>;
+type ApiResult<T> = Result<T, crate::error::Error>;
+pub type JsonResult = ApiResult<Json<Value>>;
+pub type EmptyResult = ApiResult<()>;
 
 use crate::util;
 type JsonUpcase<T> = Json<util::UpCase<T>>;
diff --git a/src/db/models/attachment.rs b/src/db/models/attachment.rs
index 6530fafcc0989542d0cf8562aa10955e382c0cd3..d7881c286703e54843c9bb4d99390e407b347ac5 100644
--- a/src/db/models/attachment.rs
+++ b/src/db/models/attachment.rs
@@ -12,7 +12,7 @@ pub struct Attachment {
     pub cipher_uuid: String,
     pub file_name: String,
     pub file_size: i32,
-    pub key: Option<String>
+    pub key: Option<String>,
 }
 
 /// Local methods
@@ -23,7 +23,7 @@ impl Attachment {
             cipher_uuid,
             file_name,
             file_size,
-            key: None
+            key: None,
         }
     }
 
@@ -54,29 +54,31 @@ use diesel::prelude::*;
 use crate::db::DbConn;
 use crate::db::schema::attachments;
 
+use crate::api::EmptyResult;
+use crate::error::MapResult;
+
 /// Database methods
 impl Attachment {
-    pub fn save(&self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&self, conn: &DbConn) -> EmptyResult {
         diesel::replace_into(attachments::table)
             .values(self)
             .execute(&**conn)
-            .and(Ok(()))
+            .map_res("Error saving attachment")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         crate::util::retry(
-            || {
-                diesel::delete(attachments::table.filter(attachments::id.eq(&self.id)))
-                    .execute(&**conn)
-            },
+            || diesel::delete(attachments::table.filter(attachments::id.eq(&self.id)))
+                .execute(&**conn),
             10,
-        )?;
-
+        )
+        .map_res("Error deleting attachment")?;
+        
         crate::util::delete_file(&self.get_file_path());
         Ok(())
     }
 
-    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
         for attachment in Attachment::find_by_cipher(&cipher_uuid, &conn) {
             attachment.delete(&conn)?;
         }
@@ -84,20 +86,20 @@ impl Attachment {
     }
 
     pub fn find_by_id(id: &str, conn: &DbConn) -> Option<Self> {
-        attachments::table
-            .filter(attachments::id.eq(id))
-            .first::<Self>(&**conn).ok()
+        attachments::table.filter(attachments::id.eq(id)).first::<Self>(&**conn).ok()
     }
 
     pub fn find_by_cipher(cipher_uuid: &str, conn: &DbConn) -> Vec<Self> {
         attachments::table
             .filter(attachments::cipher_uuid.eq(cipher_uuid))
-            .load::<Self>(&**conn).expect("Error loading attachments")
+            .load::<Self>(&**conn)
+            .expect("Error loading attachments")
     }
 
     pub fn find_by_ciphers(cipher_uuids: Vec<String>, conn: &DbConn) -> Vec<Self> {
         attachments::table
             .filter(attachments::cipher_uuid.eq_any(cipher_uuids))
-            .load::<Self>(&**conn).expect("Error loading attachments")
+            .load::<Self>(&**conn)
+            .expect("Error loading attachments")
     }
 }
diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs
index 918252fe8563e4b1e154b704845f8fe482941043..40bbeb7128e21397a2ff5f4e2ed27e65c9f9d31e 100644
--- a/src/db/models/cipher.rs
+++ b/src/db/models/cipher.rs
@@ -1,7 +1,7 @@
 use chrono::{NaiveDateTime, Utc};
 use serde_json::Value;
 
-use super::{User, Organization, Attachment, FolderCipher, CollectionCipher, UserOrganization, UserOrgType, UserOrgStatus};
+use super::{Attachment, CollectionCipher, FolderCipher, Organization, User, UserOrgStatus, UserOrgType, UserOrganization};
 
 #[derive(Debug, Identifiable, Queryable, Insertable, Associations)]
 #[table_name = "ciphers"]
@@ -59,17 +59,20 @@ impl Cipher {
     }
 }
 
+use crate::db::schema::*;
+use crate::db::DbConn;
 use diesel;
 use diesel::prelude::*;
-use crate::db::DbConn;
-use crate::db::schema::*;
+
+use crate::api::EmptyResult;
+use crate::error::MapResult;
 
 /// Database methods
 impl Cipher {
     pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> Value {
-        use serde_json;
-        use crate::util::format_date;
         use super::Attachment;
+        use crate::util::format_date;
+        use serde_json;
 
         let attachments = Attachment::find_by_cipher(&self.uuid, conn);
         let attachments_json: Vec<Value> = attachments.iter().map(|c| c.to_json(host)).collect();
@@ -149,56 +152,54 @@ impl Cipher {
         user_uuids
     }
 
-    pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
         self.update_users_revision(conn);
         self.updated_at = Utc::now().naive_utc();
 
         diesel::replace_into(ciphers::table)
             .values(&*self)
             .execute(&**conn)
-            .and(Ok(()))
+            .map_res("Error saving cipher")
     }
 
-    pub fn delete(&self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(&self, conn: &DbConn) -> EmptyResult {
         self.update_users_revision(conn);
 
         FolderCipher::delete_all_by_cipher(&self.uuid, &conn)?;
         CollectionCipher::delete_all_by_cipher(&self.uuid, &conn)?;
         Attachment::delete_all_by_cipher(&self.uuid, &conn)?;
 
-        diesel::delete(
-            ciphers::table.filter(
-                ciphers::uuid.eq(&self.uuid)
-            )
-        ).execute(&**conn).and(Ok(()))
+        diesel::delete(ciphers::table.filter(ciphers::uuid.eq(&self.uuid)))
+            .execute(&**conn)
+            .map_res("Error deleting cipher")
     }
 
-    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
         for cipher in Self::find_by_org(org_uuid, &conn) {
             cipher.delete(&conn)?;
         }
         Ok(())
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         for cipher in Self::find_owned_by_user(user_uuid, &conn) {
             cipher.delete(&conn)?;
         }
         Ok(())
     }
 
-    pub fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> Result<(), &str> {
-        match self.get_folder_uuid(&user_uuid, &conn)  {
+    pub fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
+        match self.get_folder_uuid(&user_uuid, &conn) {
             None => {
                 match folder_uuid {
                     Some(new_folder) => {
                         self.update_users_revision(conn);
                         let folder_cipher = FolderCipher::new(&new_folder, &self.uuid);
-                        folder_cipher.save(&conn).or(Err("Couldn't save folder setting"))
-                    },
-                    None => Ok(()) //nothing to do
+                        folder_cipher.save(&conn)
+                    }
+                    None => Ok(()), //nothing to do
                 }
-            },
+            }
             Some(current_folder) => {
                 match folder_uuid {
                     Some(new_folder) => {
@@ -206,24 +207,17 @@ impl Cipher {
                             Ok(()) //nothing to do
                         } else {
                             self.update_users_revision(conn);
-                            match FolderCipher::find_by_folder_and_cipher(&current_folder, &self.uuid, &conn) {
-                                Some(current_folder) => {
-                                    current_folder.delete(&conn).or(Err("Failed removing old folder mapping"))
-                                },
-                                None => Ok(()) // Weird, but nothing to do
-                            }.and_then(
-                                |()| FolderCipher::new(&new_folder, &self.uuid)
-                                .save(&conn).or(Err("Couldn't save folder setting"))
-                            )
+                            if let Some(current_folder) = FolderCipher::find_by_folder_and_cipher(&current_folder, &self.uuid, &conn) {
+                                current_folder.delete(&conn)?;
+                            }
+                            FolderCipher::new(&new_folder, &self.uuid).save(&conn)
                         }
-                    },
+                    }
                     None => {
                         self.update_users_revision(conn);
                         match FolderCipher::find_by_folder_and_cipher(&current_folder, &self.uuid, &conn) {
-                            Some(current_folder) => {
-                                current_folder.delete(&conn).or(Err("Failed removing old folder mapping"))
-                            },
-                            None => Err("Couldn't move from previous folder")
+                            Some(current_folder) => current_folder.delete(&conn),
+                            None => err!("Couldn't move from previous folder"),
                         }
                     }
                 }
diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs
index a9e0969a3ccdcbde4318b63866d8bcd66a979f43..7b0f565c6a412fd2918d7b6c882993f555672532 100644
--- a/src/db/models/collection.rs
+++ b/src/db/models/collection.rs
@@ -38,9 +38,12 @@ use diesel::prelude::*;
 use crate::db::DbConn;
 use crate::db::schema::*;
 
+use crate::api::EmptyResult;
+use crate::error::MapResult;
+
 /// Database methods
 impl Collection {
-    pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
         // Update affected users revision
         UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn)
         .iter()
@@ -51,10 +54,10 @@ impl Collection {
         diesel::replace_into(collections::table)
         .values(&*self)
         .execute(&**conn)
-        .and(Ok(()))
+        .map_res("Error saving collection")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         CollectionCipher::delete_all_by_collection(&self.uuid, &conn)?;
         CollectionUser::delete_all_by_collection(&self.uuid, &conn)?;
 
@@ -62,10 +65,11 @@ impl Collection {
             collections::table.filter(
                 collections::uuid.eq(self.uuid)
             )
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error deleting collection")
     }
 
-    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
         for collection in Self::find_by_organization(org_uuid, &conn) {
             collection.delete(&conn)?;
         }
@@ -185,7 +189,7 @@ impl CollectionUser {
             .load::<Self>(&**conn).expect("Error loading users_collections")
     }
 
-    pub fn save(user_uuid: &str, collection_uuid: &str, read_only:bool, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(user_uuid: &str, collection_uuid: &str, read_only:bool, conn: &DbConn) -> EmptyResult {
         User::update_uuid_revision(&user_uuid, conn);
 
         diesel::replace_into(users_collections::table)
@@ -193,16 +197,18 @@ impl CollectionUser {
             users_collections::user_uuid.eq(user_uuid),
             users_collections::collection_uuid.eq(collection_uuid),
             users_collections::read_only.eq(read_only),
-        )).execute(&**conn).and(Ok(()))
+        )).execute(&**conn)
+        .map_res("Error adding user to collection")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         User::update_uuid_revision(&self.user_uuid, conn);
 
         diesel::delete(users_collections::table
         .filter(users_collections::user_uuid.eq(&self.user_uuid))
         .filter(users_collections::collection_uuid.eq(&self.collection_uuid)))
-        .execute(&**conn).and(Ok(()))
+        .execute(&**conn)
+        .map_res("Error removing user from collection")
     }
 
     pub fn find_by_collection(collection_uuid: &str, conn: &DbConn) -> Vec<Self> {
@@ -220,7 +226,7 @@ impl CollectionUser {
         .first::<Self>(&**conn).ok()
     }
 
-    pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
         CollectionUser::find_by_collection(&collection_uuid, conn)
         .iter()
         .for_each(|collection| {
@@ -229,15 +235,17 @@ impl CollectionUser {
 
         diesel::delete(users_collections::table
             .filter(users_collections::collection_uuid.eq(collection_uuid))
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error deleting users from collection")
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         User::update_uuid_revision(&user_uuid, conn);
 
         diesel::delete(users_collections::table
             .filter(users_collections::user_uuid.eq(user_uuid))
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error removing user from collections")
     }
 }
 
@@ -255,30 +263,34 @@ pub struct CollectionCipher {
 
 /// Database methods
 impl CollectionCipher {
-    pub fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult {
         diesel::replace_into(ciphers_collections::table)
             .values((
                 ciphers_collections::cipher_uuid.eq(cipher_uuid),
                 ciphers_collections::collection_uuid.eq(collection_uuid),
-            )).execute(&**conn).and(Ok(()))
+            )).execute(&**conn)
+            .map_res("Error adding cipher to collection")
     }
 
-    pub fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(cipher_uuid: &str, collection_uuid: &str, conn: &DbConn) -> EmptyResult {
         diesel::delete(ciphers_collections::table
             .filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))
             .filter(ciphers_collections::collection_uuid.eq(collection_uuid)))
-            .execute(&**conn).and(Ok(()))
+            .execute(&**conn)
+            .map_res("Error deleting cipher from collection")
     }
 
-    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
         diesel::delete(ciphers_collections::table
             .filter(ciphers_collections::cipher_uuid.eq(cipher_uuid))
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error removing cipher from collections")
     }
 
-    pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
         diesel::delete(ciphers_collections::table
             .filter(ciphers_collections::collection_uuid.eq(collection_uuid))
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error removing ciphers from collection")
     }
 }
\ No newline at end of file
diff --git a/src/db/models/device.rs b/src/db/models/device.rs
index 7f2552e4f0499b8b66da92b1eca912f94e1efd65..2636b9355da38967950b5c17cb153afd1d50c7aa 100644
--- a/src/db/models/device.rs
+++ b/src/db/models/device.rs
@@ -110,9 +110,12 @@ use diesel::prelude::*;
 use crate::db::DbConn;
 use crate::db::schema::devices;
 
+use crate::api::EmptyResult;
+use crate::error::MapResult;
+
 /// Database methods
 impl Device {
-    pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
         self.updated_at = Utc::now().naive_utc();
 
         crate::util::retry(
@@ -123,16 +126,17 @@ impl Device {
             },
             10,
         )
-        .and(Ok(()))
+        .map_res("Error saving device")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         diesel::delete(devices::table.filter(
             devices::uuid.eq(self.uuid)
-        )).execute(&**conn).and(Ok(()))
+        )).execute(&**conn)
+        .map_res("Error removing device")
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         for device in Self::find_by_user(user_uuid, &conn) {
             device.delete(&conn)?;
         }
diff --git a/src/db/models/folder.rs b/src/db/models/folder.rs
index 3a932b57cf246f7abba521087a4d2ba1e9abc9a3..669a9c3bf59f7e3bb3988bc79385611b1e2dd96b 100644
--- a/src/db/models/folder.rs
+++ b/src/db/models/folder.rs
@@ -66,17 +66,21 @@ use diesel::prelude::*;
 use crate::db::DbConn;
 use crate::db::schema::{folders, folders_ciphers};
 
+use crate::api::EmptyResult;
+use crate::error::MapResult;
+
 /// Database methods
 impl Folder {
-    pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
         User::update_uuid_revision(&self.user_uuid, conn);
         self.updated_at = Utc::now().naive_utc();
 
         diesel::replace_into(folders::table)
-            .values(&*self).execute(&**conn).and(Ok(()))
+            .values(&*self).execute(&**conn)    
+        .map_res("Error saving folder")
     }
 
-    pub fn delete(&self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(&self, conn: &DbConn) -> EmptyResult {
         User::update_uuid_revision(&self.user_uuid, conn);
         FolderCipher::delete_all_by_folder(&self.uuid, &conn)?;
 
@@ -84,10 +88,11 @@ impl Folder {
             folders::table.filter(
                 folders::uuid.eq(&self.uuid)
             )
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error deleting folder")
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         for folder in Self::find_by_user(user_uuid, &conn) {
             folder.delete(&conn)?;
         }
@@ -108,29 +113,33 @@ impl Folder {
 }
 
 impl FolderCipher {
-    pub fn save(&self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&self, conn: &DbConn) -> EmptyResult {
         diesel::replace_into(folders_ciphers::table)
         .values(&*self)
-        .execute(&**conn).and(Ok(()))
+        .execute(&**conn)
+        .map_res("Error adding cipher to folder")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         diesel::delete(folders_ciphers::table
             .filter(folders_ciphers::cipher_uuid.eq(self.cipher_uuid))
             .filter(folders_ciphers::folder_uuid.eq(self.folder_uuid))
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error removing cipher from folder")
     }
 
-    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> EmptyResult {
         diesel::delete(folders_ciphers::table
             .filter(folders_ciphers::cipher_uuid.eq(cipher_uuid))
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error removing cipher from folders")
     }
 
-    pub fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_folder(folder_uuid: &str, conn: &DbConn) -> EmptyResult {
         diesel::delete(folders_ciphers::table
             .filter(folders_ciphers::folder_uuid.eq(folder_uuid))
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error removing ciphers from folder")
     }
 
     pub fn find_by_folder_and_cipher(folder_uuid: &str, cipher_uuid: &str, conn: &DbConn) -> Option<Self> {
diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs
index a83a9725c08e66fbb8eb44fce0d73fb414f0eec9..808e16ecdab25a211de0b20b2e1a34d45b256d0d 100644
--- a/src/db/models/organization.rs
+++ b/src/db/models/organization.rs
@@ -238,11 +238,14 @@ use diesel::prelude::*;
 use crate::db::DbConn;
 use crate::db::schema::{organizations, users_organizations, users_collections, ciphers_collections};
 
+use crate::api::EmptyResult;
+use crate::error::MapResult;
+
 /// Database methods
 impl Organization {
-    pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
         if self.uuid == Organization::VIRTUAL_ID {
-            return Err(diesel::result::Error::NotFound)
+            err!("diesel::result::Error::NotFound")
         }
 
         UserOrganization::find_by_org(&self.uuid, conn)
@@ -252,14 +255,15 @@ impl Organization {
         });
 
         diesel::replace_into(organizations::table)
-            .values(&*self).execute(&**conn).and(Ok(()))
+            .values(&*self).execute(&**conn)
+            .map_res("Error saving organization")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         use super::{Cipher, Collection};
 
         if self.uuid == Organization::VIRTUAL_ID {
-            return Err(diesel::result::Error::NotFound)
+            err!("diesel::result::Error::NotFound")
         }
 
         Cipher::delete_all_by_organization(&self.uuid, &conn)?;
@@ -270,7 +274,8 @@ impl Organization {
             organizations::table.filter(
                 organizations::uuid.eq(self.uuid)
             )
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error saving organization")
     }
 
     pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
@@ -365,19 +370,20 @@ impl UserOrganization {
         })
     }
 
-    pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
         if self.org_uuid == Organization::VIRTUAL_ID {
-            return Err(diesel::result::Error::NotFound)
+            err!("diesel::result::Error::NotFound")
         }
         User::update_uuid_revision(&self.user_uuid, conn);
 
         diesel::replace_into(users_organizations::table)
-            .values(&*self).execute(&**conn).and(Ok(()))
+            .values(&*self).execute(&**conn)  
+        .map_res("Error adding user to organization")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         if self.org_uuid == Organization::VIRTUAL_ID {
-            return Err(diesel::result::Error::NotFound)
+            err!("diesel::result::Error::NotFound")
         }
         User::update_uuid_revision(&self.user_uuid, conn);
 
@@ -387,17 +393,18 @@ impl UserOrganization {
             users_organizations::table.filter(
                 users_organizations::uuid.eq(self.uuid)
             )
-        ).execute(&**conn).and(Ok(()))
+        ).execute(&**conn)
+        .map_res("Error removing user from organization")
     }
 
-    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
         for user_org in Self::find_by_org(&org_uuid, &conn) {
             user_org.delete(&conn)?;
         }
         Ok(())
     }
 
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         for user_org in Self::find_any_state_by_user(&user_uuid, &conn) {
             user_org.delete(&conn)?;
         }
diff --git a/src/db/models/two_factor.rs b/src/db/models/two_factor.rs
index c3945aab0dd9fc519c73effc7ba9aaf085e3b2fc..609cb7b787f432ecd873075efba461139445df50 100644
--- a/src/db/models/two_factor.rs
+++ b/src/db/models/two_factor.rs
@@ -79,20 +79,25 @@ use diesel::prelude::*;
 use crate::db::DbConn;
 use crate::db::schema::twofactor;
 
+use crate::api::EmptyResult;
+use crate::error::MapResult;
+
 /// Database methods
 impl TwoFactor {
-    pub fn save(&self, conn: &DbConn) -> QueryResult<usize> {
+    pub fn save(&self, conn: &DbConn) -> EmptyResult {
         diesel::replace_into(twofactor::table)
             .values(self)
             .execute(&**conn)
+            .map_res("Error saving twofactor")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<usize> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         diesel::delete(
             twofactor::table.filter(
                 twofactor::uuid.eq(self.uuid)
             )
         ).execute(&**conn)
+        .map_res("Error deleting twofactor")
     }
 
     pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
@@ -108,11 +113,12 @@ impl TwoFactor {
             .first::<Self>(&**conn).ok()
     }
     
-    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> QueryResult<usize> {
+    pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         diesel::delete(
             twofactor::table.filter(
                 twofactor::user_uuid.eq(user_uuid)
             )
         ).execute(&**conn)
+        .map_res("Error deleting twofactors")
     }
 }
diff --git a/src/db/models/user.rs b/src/db/models/user.rs
index a581c76e0a7249bc284358d1f74be67b573b4d71..b1cd74fb42128908bd204a23431f5bea7d7cb8f6 100644
--- a/src/db/models/user.rs
+++ b/src/db/models/user.rs
@@ -115,6 +115,9 @@ use crate::db::DbConn;
 use crate::db::schema::{users, invitations};
 use super::{Cipher, Folder, Device, UserOrganization, UserOrgType, TwoFactor};
 
+use crate::api::EmptyResult;
+use crate::error::MapResult;
+
 /// Database methods
 impl User {
     pub fn to_json(&self, conn: &DbConn) -> Value {
@@ -145,21 +148,22 @@ impl User {
     }
 
 
-    pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
         self.updated_at = Utc::now().naive_utc();
 
         diesel::replace_into(users::table) // Insert or update
-            .values(&*self).execute(&**conn).and(Ok(()))
+            .values(&*self).execute(&**conn)
+        .map_res("Error saving user")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         for user_org in UserOrganization::find_by_user(&self.uuid, &*conn) {
             if user_org.type_ == UserOrgType::Owner {
                 if UserOrganization::find_by_org_and_type(
                     &user_org.org_uuid, 
                     UserOrgType::Owner as i32, &conn
                 ).len() <= 1 {
-                    return Err(diesel::result::Error::NotFound);
+                    err!("Can't delete last owner")
                 }
             }
         }
@@ -168,12 +172,13 @@ impl User {
         Cipher::delete_all_by_user(&self.uuid, &*conn)?;
         Folder::delete_all_by_user(&self.uuid, &*conn)?;
         Device::delete_all_by_user(&self.uuid, &*conn)?;
-        TwoFactor::delete_all_by_user(&self.uuid, &*conn)?;
+        //TwoFactor::delete_all_by_user(&self.uuid, &*conn)?;
         Invitation::take(&self.email, &*conn); // Delete invitation if any
 
         diesel::delete(users::table.filter(
         users::uuid.eq(self.uuid)))
-        .execute(&**conn).and(Ok(()))
+        .execute(&**conn)
+        .map_res("Error deleting user")
     }
 
     pub fn update_uuid_revision(uuid: &str, conn: &DbConn) {
@@ -184,7 +189,7 @@ impl User {
         };
     }
 
-    pub fn update_revision(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn update_revision(&mut self, conn: &DbConn) -> EmptyResult {
         self.updated_at = Utc::now().naive_utc();
         diesel::update(
             users::table.filter(
@@ -192,7 +197,8 @@ impl User {
             )
         )
         .set(users::updated_at.eq(&self.updated_at))
-        .execute(&**conn).and(Ok(()))
+        .execute(&**conn)
+        .map_res("Error updating user revision")
     }
 
     pub fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
@@ -228,18 +234,18 @@ impl Invitation {
         }
     }
 
-    pub fn save(&mut self, conn: &DbConn) -> QueryResult<()> {
+    pub fn save(&mut self, conn: &DbConn) -> EmptyResult {
         diesel::replace_into(invitations::table)
         .values(&*self)
         .execute(&**conn)
-        .and(Ok(()))
+        .map_res("Error saving invitation")
     }
 
-    pub fn delete(self, conn: &DbConn) -> QueryResult<()> {
+    pub fn delete(self, conn: &DbConn) -> EmptyResult {
         diesel::delete(invitations::table.filter(
         invitations::email.eq(self.email)))
         .execute(&**conn)
-        .and(Ok(()))
+        .map_res("Error deleting invitation")
     }
 
     pub fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> {
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000000000000000000000000000000000000..905bf63c87d0a360e03ee2f3f252dd69eec6d7f0
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,156 @@
+//
+// Error generator macro
+//
+macro_rules! make_error {
+    ( $struct:ident; $( $name:ident ( $ty:ty, _): $show_cause:expr, $usr_msg_fun:expr ),+ $(,)* ) => {
+        #[derive(Debug)]
+        #[allow(unused_variables, dead_code)]
+        pub enum $struct {
+            $($name( $ty, String )),+
+        }
+        $(impl From<$ty> for $struct {
+            fn from(err: $ty) -> Self {
+                $struct::$name(err, String::from(stringify!($name)))
+            }
+        })+
+        $(impl From<($ty, String)> for $struct {
+            fn from(err: ($ty, String)) -> Self {
+                $struct::$name(err.0, err.1)
+            }
+        })+
+        impl $struct {
+            pub fn with_msg<M: Into<String>>(self, msg: M) -> Self {
+                match self {$(
+                   $struct::$name(e, _) => $struct::$name(e, msg.into()),
+                )+}
+            }
+            // First value is log message, second is user message
+            pub fn display_error(self) -> String {
+                match &self {$(
+                   $struct::$name(e, s) => {
+                       let log_msg = format!("{}. {}", &s, &e);
+
+                        error!("{}", log_msg);
+                        if $show_cause {
+                            error!("[CAUSE] {:?}", e);
+                        }
+
+                        $usr_msg_fun(e, s)
+                   },
+                )+}
+            }
+        }
+
+    };
+}
+
+use diesel::result::{Error as DieselError, QueryResult};
+use serde_json::{Value, Error as SerError};
+use u2f::u2ferror::U2fError as U2fErr;
+
+// Error struct
+// Each variant has two elements, the first is an error of different types, used for logging purposes
+// The second is a String, and it's contents are displayed to the user when the error occurs. Inside the macro, this is represented as _
+// 
+// After the variant itself, there are two expressions. The first one is a bool to indicate whether the error cause will be printed to the log.
+// The second one contains the function used to obtain the response sent to the client
+make_error! {
+    Error;
+    // Used to represent err! calls
+    SimpleError(String,  _): false, _api_error,
+    // Used for special return values, like 2FA errors
+    JsonError(Value,     _): false, _serialize,
+    DbError(DieselError, _): true,  _api_error,
+    U2fError(U2fErr,     _): true,  _api_error,
+    SerdeError(SerError, _): true,  _api_error,
+    //WsError(ws::Error, _): true,  _api_error,
+}
+
+impl Error {
+    pub fn new<M: Into<String>, N: Into<String>>(usr_msg: M, log_msg: N) -> Self {
+        Error::SimpleError(log_msg.into(), usr_msg.into())
+    }
+}
+
+pub trait MapResult<S, E> {
+    fn map_res(self, msg: &str) -> Result<(), E>;
+}
+
+impl MapResult<(), Error> for QueryResult<usize> {
+    fn map_res(self, msg: &str) -> Result<(), Error> {
+        self.and(Ok(())).map_err(Error::from).map_err(|e| e.with_msg(msg))
+    }
+}
+
+use serde::Serialize;
+use std::any::Any;
+
+fn _serialize(e: &impl Serialize, _: &impl Any) -> String {
+    serde_json::to_string(e).unwrap()
+}
+
+fn _api_error(_: &impl Any, msg: &str) -> String {
+    let json = json!({
+        "Message": "",
+        "error": "",
+        "error_description": "",
+        "ValidationErrors": {"": [ msg ]},
+        "ErrorModel": {
+            "Message": msg,
+            "Object": "error"
+        },
+        "Object": "error"
+    });
+
+    _serialize(&json, &false)
+}
+
+//
+// Rocket responder impl
+//
+use std::io::Cursor;
+
+use rocket::http::{ContentType, Status};
+use rocket::request::Request;
+use rocket::response::{self, Responder, Response};
+
+impl<'r> Responder<'r> for Error {
+    fn respond_to(self, _: &Request) -> response::Result<'r> {
+        // TODO: We could put the security headers here
+
+        let usr_msg = self.display_error();
+
+        Response::build()
+            .status(Status::BadRequest)
+            .header(ContentType::JSON)
+            .sized_body(Cursor::new(usr_msg))
+            .ok()
+    }
+}
+
+///
+/// Error return macros
+///
+#[macro_export]
+macro_rules! err {
+    ($msg:expr) => {{
+        return Err(crate::error::Error::new($msg, $msg));
+    }};
+    ($usr_msg:expr, $log_value:expr) => {{
+        return Err(crate::error::Error::new($usr_msg, $log_value));
+    }};
+}
+
+#[macro_export]
+macro_rules! err_json {
+    ($expr:expr) => {{
+        return Err(crate::error::Error::from($expr));
+    }};
+}
+
+#[macro_export]
+macro_rules! err_handler {
+    ($expr:expr) => {{
+        return rocket::Outcome::Failure((rocket::http::Status::Unauthorized, $expr));
+    }};
+}
diff --git a/src/mail.rs b/src/mail.rs
index 485e5c1757ed9899b644b8174e03c5f5ffa00713..c993efb0ca0df044b7db89044a96859a1a99a3e4 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -7,6 +7,9 @@ use lettre_email::EmailBuilder;
 use crate::MailConfig;
 use crate::CONFIG;
 
+use crate::api::EmptyResult;
+use crate::error::Error;
+
 fn mailer(config: &MailConfig) -> SmtpTransport {
     let client_security = if config.smtp_ssl {
         let tls = TlsConnector::builder()
@@ -35,7 +38,7 @@ fn mailer(config: &MailConfig) -> SmtpTransport {
         .transport()
 }
 
-pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConfig) -> Result<(), String> {
+pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConfig) -> EmptyResult {
     let (subject, body) = if let Some(hint) = hint {
         ("Your master password hint",
          format!(
@@ -54,11 +57,11 @@ pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConf
         .subject(subject)
         .body(body)
         .build()
-        .map_err(|e| e.to_string())?;
+        .map_err(|e| Error::new("Error building hint email", e.to_string()))?;
 
     mailer(config)
         .send(email.into())
-        .map_err(|e| e.to_string())
+        .map_err(|e| Error::new("Error sending hint email", e.to_string()))
         .and(Ok(()))
 }
 
diff --git a/src/main.rs b/src/main.rs
index 83c2b39896a8da20287ff2f563d77f7fa4472af1..c4fe9d2bb6a8bd19f0890c9b16ba98511da6272d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,9 +14,8 @@
 use std::{path::Path, process::{exit, Command}};
 use rocket::Rocket;
 
-#[macro_use]
+#[macro_use] mod error;
 mod util;
-
 mod api;
 mod db;
 mod crypto;
diff --git a/src/static/admin.html b/src/static/admin.html
index 44d5069b273bdb99d5e640b84145044556f5a4ef..d76b14382b384c10bf93047abb2103f64f0b41f7 100644
--- a/src/static/admin.html
+++ b/src/static/admin.html
@@ -42,7 +42,7 @@
         function updateVis() {
             setVis("#no-key-form", !key);
             setVis("#users-block", key);
-            setVis("#invite-form", key);
+            setVis("#invite-form-block", key);
         }
 
         function setKey() {
@@ -166,7 +166,7 @@
             </small>
         </div>
 
-        <div id="invite-form" class="d-none align-items-center p-3 mb-3 text-white-50 bg-secondary rounded shadow">
+        <div id="invite-form-block" class="d-none align-items-center p-3 mb-3 text-white-50 bg-secondary rounded shadow">
             <div>
                 <h6 class="mb-0 text-white">Invite User</h6>
                 <small>Email:</small>
diff --git a/src/util.rs b/src/util.rs
index a4d96a3cac422131849eeb6404d0a1a641742c74..9a2a6073d8f32899e7bc5afbfe57308cecc9e7a4 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,50 +1,3 @@
-///
-/// Macros
-///
-#[macro_export]
-macro_rules! _err_object {
-    ($msg:expr) => {{
-        err_json!(json!({
-            "Message": "",
-            "error": "",
-            "error_description": "",
-            "ValidationErrors": {"": [ $msg ]},
-            "ErrorModel": {
-                "Message": $msg,
-                "Object": "error"
-            },
-            "Object": "error"
-        }))
-    }};
-}
-
-#[macro_export]
-macro_rules! err {
-    ($msg:expr) => {{
-        error!("{}", $msg);
-        _err_object!($msg)
-    }};
-    ($usr_msg:expr, $log_value:expr) => {{
-        error!("{}: {:#?}", $usr_msg, $log_value);
-        _err_object!($usr_msg)
-    }}
-}
-
-#[macro_export]
-macro_rules! err_json {
-    ($expr:expr) => {{
-        return Err(rocket::response::status::BadRequest(Some(rocket_contrib::json::Json($expr))));
-    }}
-}
-
-#[macro_export]
-macro_rules! err_handler {
-    ($expr:expr) => {{
-        error!("{}", $expr);
-        return rocket::Outcome::Failure((rocket::http::Status::Unauthorized, $expr));
-    }}
-}
-
 ///
 /// File handling
 ///