diff --git a/src/components/DotIndicatorMessage.js b/src/components/DotIndicatorMessage.js
new file mode 100644
index 000000000000..2b073627ef51
--- /dev/null
+++ b/src/components/DotIndicatorMessage.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import _ from 'underscore';
+import PropTypes from 'prop-types';
+import {View} from 'react-native';
+import styles from '../styles/styles';
+import Icon from './Icon';
+import * as Expensicons from './Icon/Expensicons';
+import colors from '../styles/colors';
+import variables from '../styles/variables';
+import Text from './Text';
+
+const propTypes = {
+    /**
+     * In most cases this should just be errors from onxyData
+     * if you are not passing that data then this needs to be in a similar shape like
+     *  {
+     *      timestamp: 'message',
+     *  }
+     */
+    messages: PropTypes.objectOf(PropTypes.string),
+
+    // The type of message, 'error' shows a red dot, 'success' shows a green dot
+    type: PropTypes.oneOf(['error', 'success']).isRequired,
+
+    // Additional styles to apply to the container */
+    // eslint-disable-next-line react/forbid-prop-types
+    style: PropTypes.arrayOf(PropTypes.object),
+};
+
+const defaultProps = {
+    messages: {},
+    style: [],
+};
+
+const DotIndicatorMessage = (props) => {
+    if (_.isEmpty(props.messages)) {
+        return null;
+    }
+
+    // To ensure messages are presented in order we are sort of destroying the data we are given
+    // and rebuilding as an array so we can render the messages in order. We don't really care about
+    // the microtime timestamps anyways so isn't the end of the world that we sort of lose them here.
+    // BEWARE: if you decide to refactor this and keep the microtime keys it could cause performance issues
+    const sortedMessages = _.chain(props.messages)
+        .keys()
+        .sortBy()
+        .map(key => props.messages[key])
+        .value();
+
+    return (
+        <View style={[styles.dotIndicatorMessage, ...props.style]}>
+            <View style={styles.offlineFeedback.errorDot}>
+                <Icon src={Expensicons.DotIndicator} fill={props.type === 'error' ? colors.red : colors.green} height={variables.iconSizeSmall} width={variables.iconSizeSmall} />
+            </View>
+            <View style={styles.offlineFeedback.textContainer}>
+                {_.map(sortedMessages, (message, i) => (
+                    <Text key={i} style={styles.offlineFeedback.text}>{message}</Text>
+                ))}
+            </View>
+        </View>
+    );
+};
+
+DotIndicatorMessage.propTypes = propTypes;
+DotIndicatorMessage.defaultProps = defaultProps;
+
+export default DotIndicatorMessage;
+
diff --git a/src/components/OfflineWithFeedback.js b/src/components/OfflineWithFeedback.js
index bd0eb8a8b6f7..54d6bc0ab9be 100644
--- a/src/components/OfflineWithFeedback.js
+++ b/src/components/OfflineWithFeedback.js
@@ -7,14 +7,12 @@ import withLocalize, {withLocalizePropTypes} from './withLocalize';
 import {withNetwork} from './OnyxProvider';
 import networkPropTypes from './networkPropTypes';
 import stylePropTypes from '../styles/stylePropTypes';
-import Text from './Text';
 import styles from '../styles/styles';
 import Tooltip from './Tooltip';
 import Icon from './Icon';
 import * as Expensicons from './Icon/Expensicons';
 import * as StyleUtils from '../styles/StyleUtils';
-import colors from '../styles/colors';
-import variables from '../styles/variables';
+import DotIndicatorMessage from './DotIndicatorMessage';
 
 /**
  * This component should be used when we are using the offline pattern B (offline with feedback).
@@ -83,11 +81,6 @@ const OfflineWithFeedback = (props) => {
     const needsStrikeThrough = props.network.isOffline && props.pendingAction === 'delete';
     const hideChildren = !props.network.isOffline && props.pendingAction === 'delete' && !hasErrors;
     let children = props.children;
-    const sortedErrors = _.chain(props.errors)
-        .keys()
-        .sortBy()
-        .map(key => props.errors[key])
-        .value();
 
     // Apply strikethrough to children if needed, but skip it if we are not going to render them
     if (needsStrikeThrough && !hideChildren) {
@@ -102,14 +95,7 @@ const OfflineWithFeedback = (props) => {
             )}
             {hasErrors && (
                 <View style={StyleUtils.combineStyles(styles.offlineFeedback.error, props.errorRowStyles)}>
-                    <View style={styles.offlineFeedback.errorDot}>
-                        <Icon src={Expensicons.DotIndicator} fill={colors.red} height={variables.iconSizeSmall} width={variables.iconSizeSmall} />
-                    </View>
-                    <View style={styles.offlineFeedback.textContainer}>
-                        {_.map(sortedErrors, (error, i) => (
-                            <Text key={i} style={styles.offlineFeedback.text}>{error}</Text>
-                        ))}
-                    </View>
+                    <DotIndicatorMessage messages={props.errors} type="error" />
                     <Tooltip text={props.translate('common.close')}>
                         <Pressable
                             onPress={props.onClose}
diff --git a/src/libs/ErrorUtils.js b/src/libs/ErrorUtils.js
index 115468ff1ea6..bcc6c59cae2b 100644
--- a/src/libs/ErrorUtils.js
+++ b/src/libs/ErrorUtils.js
@@ -1,3 +1,4 @@
+import _ from 'underscore';
 import CONST from '../CONST';
 
 /**
@@ -35,7 +36,23 @@ function getAuthenticateErrorMessage(response) {
     }
 }
 
+/**
+ * @param {Object} onyxData
+ * @param {Object} onyxData.errors
+ * @returns {String}
+ */
+function getLatestErrorMessage(onyxData) {
+    return _.chain(onyxData.errors || [])
+        .keys()
+        .sortBy()
+        .reverse()
+        .map(key => onyxData.errors[key])
+        .first()
+        .value();
+}
+
 export {
     // eslint-disable-next-line import/prefer-default-export
     getAuthenticateErrorMessage,
+    getLatestErrorMessage,
 };
diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js
index 8e191c164fe6..6342b3d05ebf 100644
--- a/src/libs/actions/Session/index.js
+++ b/src/libs/actions/Session/index.js
@@ -90,11 +90,33 @@ function signOutAndRedirectToSignIn() {
  * @param {String} [login]
  */
 function resendValidationLink(login = credentials.login) {
-    Onyx.merge(ONYXKEYS.ACCOUNT, {isLoading: true});
-    DeprecatedAPI.ResendValidateCode({email: login})
-        .finally(() => {
-            Onyx.merge(ONYXKEYS.ACCOUNT, {isLoading: false});
-        });
+    const optimisticData = [{
+        onyxMethod: CONST.ONYX.METHOD.MERGE,
+        key: ONYXKEYS.ACCOUNT,
+        value: {
+            isLoading: true,
+            errors: null,
+            message: null,
+        },
+    }];
+    const successData = [{
+        onyxMethod: CONST.ONYX.METHOD.MERGE,
+        key: ONYXKEYS.ACCOUNT,
+        value: {
+            isLoading: false,
+            message: Localize.translateLocal('resendValidationForm.linkHasBeenResent'),
+        },
+    }];
+    const failureData = [{
+        onyxMethod: CONST.ONYX.METHOD.MERGE,
+        key: ONYXKEYS.ACCOUNT,
+        value: {
+            isLoading: false,
+            message: null,
+        },
+    }];
+
+    API.write('RequestAccountValidationLink', {email: login}, {optimisticData, successData, failureData});
 }
 
 /**
diff --git a/src/pages/signin/LoginForm.js b/src/pages/signin/LoginForm.js
index f80488fb3f4a..2490103b97e3 100755
--- a/src/pages/signin/LoginForm.js
+++ b/src/pages/signin/LoginForm.js
@@ -21,6 +21,7 @@ import FormAlertWithSubmitButton from '../../components/FormAlertWithSubmitButto
 import OfflineIndicator from '../../components/OfflineIndicator';
 import {withNetwork} from '../../components/OnyxProvider';
 import networkPropTypes from '../../components/networkPropTypes';
+import * as ErrorUtils from '../../libs/ErrorUtils';
 
 const propTypes = {
     /** Should we dismiss the keyboard when transitioning away from the page? */
@@ -151,13 +152,7 @@ class LoginForm extends React.Component {
 
     render() {
         const formErrorTranslated = this.state.formError && this.props.translate(this.state.formError);
-        const error = formErrorTranslated || _.chain(this.props.account.errors || [])
-            .keys()
-            .sortBy()
-            .reverse()
-            .map(key => this.props.account.errors[key])
-            .first()
-            .value();
+        const error = formErrorTranslated || ErrorUtils.getLatestErrorMessage(this.props.account);
         return (
             <>
                 <View style={[styles.mt3]}>
diff --git a/src/pages/signin/ResendValidationForm.js b/src/pages/signin/ResendValidationForm.js
index e643e94eed73..00f1c1d629a1 100755
--- a/src/pages/signin/ResendValidationForm.js
+++ b/src/pages/signin/ResendValidationForm.js
@@ -1,8 +1,8 @@
 import React from 'react';
+import _ from 'underscore';
 import {TouchableOpacity, View} from 'react-native';
 import {withOnyx} from 'react-native-onyx';
 import PropTypes from 'prop-types';
-import _ from 'underscore';
 import Str from 'expensify-common/lib/str';
 import styles from '../../styles/styles';
 import Button from '../../components/Button';
@@ -17,6 +17,7 @@ import * as ReportUtils from '../../libs/ReportUtils';
 import OfflineIndicator from '../../components/OfflineIndicator';
 import networkPropTypes from '../../components/networkPropTypes';
 import {withNetwork} from '../../components/OnyxProvider';
+import DotIndicatorMessage from '../../components/DotIndicatorMessage';
 
 const propTypes = {
     /* Onyx Props */
@@ -46,92 +47,56 @@ const defaultProps = {
     account: {},
 };
 
-class ResendValidationForm extends React.Component {
-    constructor(props) {
-        super(props);
-
-        this.validateAndSubmitForm = this.validateAndSubmitForm.bind(this);
-
-        this.state = {
-            formSuccess: '',
-        };
-    }
-
-    componentWillUnmount() {
-        if (!this.successMessageTimer) {
-            return;
-        }
-
-        clearTimeout(this.successMessageTimer);
-    }
-
-    /**
-     * Check that all the form fields are valid, then trigger the submit callback
-     */
-    validateAndSubmitForm() {
-        this.setState({
-            formSuccess: this.props.translate('resendValidationForm.linkHasBeenResent'),
-        });
-
-        if (!this.props.account.validated) {
-            Session.resendValidationLink();
-        } else {
-            Session.resetPassword();
-        }
-
-        this.successMessageTimer = setTimeout(() => {
-            this.setState({formSuccess: ''});
-        }, 5000);
-    }
-
-    render() {
-        const isSMSLogin = Str.isSMSLogin(this.props.credentials.login);
-        const login = isSMSLogin ? this.props.toLocalPhone(Str.removeSMSDomain(this.props.credentials.login)) : this.props.credentials.login;
-        const loginType = (isSMSLogin ? this.props.translate('common.phone') : this.props.translate('common.email')).toLowerCase();
-
-        return (
-            <>
-                <View style={[styles.mt3, styles.flexRow, styles.alignItemsCenter, styles.justifyContentStart]}>
-                    <Avatar
-                        source={ReportUtils.getDefaultAvatar(this.props.credentials.login)}
-                        imageStyles={[styles.mr2]}
-                    />
-                    <View style={[styles.flex1]}>
-                        <Text style={[styles.textStrong]}>
-                            {login}
-                        </Text>
-                    </View>
-                </View>
-                <View style={[styles.mv5]}>
-                    <Text>
-                        {this.props.translate('resendValidationForm.weSentYouMagicSignInLink', {login, loginType})}
+const ResendValidationForm = (props) => {
+    const isSMSLogin = Str.isSMSLogin(props.credentials.login);
+    const login = isSMSLogin ? props.toLocalPhone(Str.removeSMSDomain(props.credentials.login)) : props.credentials.login;
+    const loginType = (isSMSLogin ? props.translate('common.phone') : props.translate('common.email')).toLowerCase();
+
+    return (
+        <>
+            <View style={[styles.mt3, styles.flexRow, styles.alignItemsCenter, styles.justifyContentStart]}>
+                <Avatar
+                    source={ReportUtils.getDefaultAvatar(props.credentials.login)}
+                    imageStyles={[styles.mr2]}
+                />
+                <View style={[styles.flex1]}>
+                    <Text style={[styles.textStrong]}>
+                        {login}
                     </Text>
                 </View>
-                {!_.isEmpty(this.state.formSuccess) && (
-                    <Text style={[styles.formSuccess]}>
-                        {this.state.formSuccess}
+            </View>
+            <View style={[styles.mv5]}>
+                <Text>
+                    {props.translate('resendValidationForm.weSentYouMagicSignInLink', {login, loginType})}
+                </Text>
+            </View>
+            {!_.isEmpty(props.account.message) && (
+
+                // DotIndicatorMessage mostly expects onyxData errors so we need to mock an object so that the messages looks similar to prop.account.errors
+                <DotIndicatorMessage style={[styles.mb5]} type="success" messages={{0: props.account.message}} />
+            )}
+            {!_.isEmpty(props.account.errors) && (
+                <DotIndicatorMessage style={[styles.mb5]} type="error" messages={props.account.errors} />
+            )}
+            <View style={[styles.mb4, styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter]}>
+                <TouchableOpacity onPress={() => redirectToSignIn()}>
+                    <Text>
+                        {props.translate('common.back')}
                     </Text>
-                )}
-                <View style={[styles.mb4, styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter]}>
-                    <TouchableOpacity onPress={() => redirectToSignIn()}>
-                        <Text>
-                            {this.props.translate('common.back')}
-                        </Text>
-                    </TouchableOpacity>
-                    <Button
-                        medium
-                        success
-                        text={this.props.translate('resendValidationForm.resendLink')}
-                        isLoading={this.props.account.loading}
-                        onPress={this.validateAndSubmitForm}
-                        isDisabled={this.props.network.isOffline}
-                    />
-                </View>
-                <OfflineIndicator containerStyles={[styles.mv1]} />
-            </>
-        );
-    }
-}
+                </TouchableOpacity>
+                <Button
+                    medium
+                    success
+                    text={props.translate('resendValidationForm.resendLink')}
+                    isLoading={props.account.isLoading}
+                    onPress={() => (props.account.validated ? Session.resetPassword() : Session.resendValidationLink())}
+                    isDisabled={props.network.isOffline}
+                />
+            </View>
+            <OfflineIndicator containerStyles={[styles.mv1]} />
+        </>
+    );
+};
 
 ResendValidationForm.propTypes = propTypes;
 ResendValidationForm.defaultProps = defaultProps;
diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js
index eca3a2bcbe5f..f687d964ab36 100644
--- a/src/pages/workspace/WorkspaceMembersPage.js
+++ b/src/pages/workspace/WorkspaceMembersPage.js
@@ -219,10 +219,10 @@ class WorkspaceMembersPage extends React.Component {
     }) {
         const canBeRemoved = this.props.policy.owner !== item.login && this.props.session.email !== item.login;
         return (
-            <OfflineWithFeedback onClose={() => this.dismissError(item)} pendingAction={item.pendingAction} errors={item.errors}>
+            <OfflineWithFeedback errorRowStyles={[styles.peopleRowBorderBottom]} onClose={() => this.dismissError(item)} pendingAction={item.pendingAction} errors={item.errors}>
                 <Hoverable onHoverIn={() => this.willTooltipShowForLogin(item.login, true)} onHoverOut={() => this.setState({showTooltipForLogin: ''})}>
                     <TouchableOpacity
-                        style={[styles.peopleRow, !canBeRemoved && styles.cursorDisabled]}
+                        style={[styles.peopleRow, !item.errors && styles.peopleRowBorderBottom, !canBeRemoved && styles.cursorDisabled]}
                         onPress={() => this.toggleUser(item.login)}
                         activeOpacity={0.7}
                     >
diff --git a/src/styles/styles.js b/src/styles/styles.js
index 8317acfa847d..51ab27573a8b 100644
--- a/src/styles/styles.js
+++ b/src/styles/styles.js
@@ -2352,9 +2352,13 @@ const styles = {
         width: '100%',
         flexDirection: 'row',
         justifyContent: 'space-between',
-        borderBottomWidth: 1,
+        ...spacing.pt2,
+    },
+
+    peopleRowBorderBottom: {
         borderColor: themeColors.border,
-        ...spacing.pv2,
+        borderBottomWidth: 1,
+        ...spacing.pb2,
     },
 
     peopleRowCell: {
@@ -2402,6 +2406,13 @@ const styles = {
         },
     },
 
+    dotIndicatorMessage: {
+        display: 'flex',
+        flex: 1,
+        flexDirection: 'row',
+        alignItems: 'center',
+    },
+
     sidebarPopover: {
         width: variables.sideBarWidth - 68,
     },
