diff --git a/packages/component-email/package.json b/packages/component-email/package.json
index f15e1b2666e0eb9c43f43294c3dc261e6891d73d..3c2a4d4c7fa08d89c8690ae8f177f0b6135d7e71 100644
--- a/packages/component-email/package.json
+++ b/packages/component-email/package.json
@@ -24,7 +24,7 @@
   },
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
-    "pubsweet-component-mail-service": "0.0.1",
+    "@pubsweet/component-send-email": "0.2.4",
     "pubsweet-server": "^1.0.1"
   },
   "devDependencies": {
diff --git a/packages/component-email/src/helpers/Email.js b/packages/component-email/src/helpers/Email.js
deleted file mode 100644
index 02a9816e095c507e4a6fe984798ed673fb929f2e..0000000000000000000000000000000000000000
--- a/packages/component-email/src/helpers/Email.js
+++ /dev/null
@@ -1,80 +0,0 @@
-const mailService = require('pubsweet-component-mail-service')
-const logger = require('@pubsweet/logger')
-const helpers = require('./helpers')
-
-module.exports = {
-  sendSignupEmail: async ({ dashboardUrl, res, email, UserModel }) => {
-    let user
-    try {
-      user = await UserModel.findByEmail(email)
-    } catch (e) {
-      const notFoundError = await helpers.handleNotFoundError(e, 'User')
-      return res.status(notFoundError.status).json({
-        error: notFoundError.message,
-      })
-    }
-    if (!user.confirmationToken) {
-      return res
-        .status(400)
-        .json({ error: 'User does not have a confirmation token.' })
-    }
-    try {
-      await mailService.sendSimpleEmail({
-        toEmail: user.email,
-        user,
-        emailType: 'signup',
-        dashboardUrl,
-        meta: {
-          confirmationToken: user.confirmationToken,
-        },
-      })
-      return res.status(200).json({})
-    } catch (e) {
-      logger.error(e)
-      return res.status(500).json({ error: 'Email could not be sent.' })
-    }
-  },
-  setupNewUserEmail: async ({ dashboardUrl, res, email, role, UserModel }) => {
-    let user
-    try {
-      user = await UserModel.findByEmail(email)
-    } catch (e) {
-      const notFoundError = await helpers.handleNotFoundError(e, 'User')
-      return res.status(notFoundError.status).json({
-        error: notFoundError.message,
-      })
-    }
-    if (!user.passwordResetToken) {
-      return res
-        .status(400)
-        .json({ error: 'User does not have a password reset token.' })
-    }
-    let emailType
-    switch (role) {
-      case 'handlingEditor':
-      case 'editorInChief':
-      case 'admin':
-        emailType = 'invite'
-        break
-      case 'author':
-        emailType = 'invite-author'
-        break
-      default:
-        return res.status(400).json({
-          error: `Role ${role} cannot be used with a password reset email.`,
-        })
-    }
-    try {
-      await mailService.sendSimpleEmail({
-        toEmail: user.email,
-        user,
-        emailType,
-        dashboardUrl,
-      })
-      return res.status(200).json({})
-    } catch (e) {
-      logger.error(e)
-      return res.status(500).json({ error: 'Email could not be sent.' })
-    }
-  },
-}
diff --git a/packages/component-email/src/helpers/helpers.js b/packages/component-email/src/helpers/helpers.js
deleted file mode 100644
index eea3de10922ae7901a4b7ad0a13a10b34100cce1..0000000000000000000000000000000000000000
--- a/packages/component-email/src/helpers/helpers.js
+++ /dev/null
@@ -1,31 +0,0 @@
-const logger = require('@pubsweet/logger')
-
-const checkForUndefinedParams = (...params) => {
-  if (params.includes(undefined)) {
-    return false
-  }
-
-  return true
-}
-
-const handleNotFoundError = async (error, item) => {
-  const response = {
-    success: false,
-    status: 500,
-    message: 'Something went wrong',
-  }
-  if (error.name === 'NotFoundError') {
-    logger.error(`invalid ${item} id`)
-    response.status = 404
-    response.message = `${item} not found.`
-    return response
-  }
-
-  logger.error(error)
-  return response
-}
-
-module.exports = {
-  checkForUndefinedParams,
-  handleNotFoundError,
-}
diff --git a/packages/component-email/src/routes/emails/emailCopy.js b/packages/component-email/src/routes/emails/emailCopy.js
new file mode 100644
index 0000000000000000000000000000000000000000..18b8ee7ccf2fde216335b7589dc65523422cb1b3
--- /dev/null
+++ b/packages/component-email/src/routes/emails/emailCopy.js
@@ -0,0 +1,19 @@
+const getEmailCopy = ({ emailType, role }) => {
+  let paragraph
+  switch (emailType) {
+    case 'user-signup':
+      paragraph = `Welcome to Hindawi. Please confirm your account by clicking on the link below.`
+      break
+    case 'user-added-by-admin':
+      paragraph = `You have been invited to join Hindawi as a ${role}. Please confirm your account and set your account details by clicking on the link below.`
+      break
+    default:
+      throw new Error(`The ${emailType} email type is not defined.`)
+  }
+
+  return { paragraph, hasLink: true }
+}
+
+module.exports = {
+  getEmailCopy,
+}
diff --git a/packages/component-email/src/routes/emails/notifications.js b/packages/component-email/src/routes/emails/notifications.js
new file mode 100644
index 0000000000000000000000000000000000000000..23fea64c842350cd283d72b55f58c1b86a920d95
--- /dev/null
+++ b/packages/component-email/src/routes/emails/notifications.js
@@ -0,0 +1,76 @@
+const config = require('config')
+
+const unsubscribeSlug = config.get('unsubscribe.url')
+const resetPath = config.get('invite-reset-password.url')
+const confirmSignUp = config.get('confirm-signup.url')
+
+const { Email, User, services } = require('pubsweet-component-helper-service')
+
+const { getEmailCopy } = require('./emailCopy')
+
+module.exports = {
+  async sendNotifications({ user, baseUrl, role, UserModel }) {
+    const userHelper = new User({ UserModel })
+    const { firstName, lastName } = await userHelper.getEditorInChief()
+    const eicName = `${firstName} ${lastName}`
+
+    const email = new Email({
+      type: 'user',
+      toUser: {
+        email: user.email,
+        name: `${user.firstName} ${user.lastName}`,
+      },
+      content: {
+        ctaLink: services.createUrl(baseUrl, resetPath, {
+          email: user.email,
+          token: user.passwordResetToken,
+          firstName: user.firstName,
+          lastName: user.lastName,
+          affiliation: user.affiliation,
+          title: user.title,
+        }),
+        ctaText: 'CONFIRM ACCOUNT',
+        signatureName: eicName,
+        unsubscribeLink: services.createUrl(baseUrl, unsubscribeSlug, {
+          id: user.id,
+        }),
+      },
+    })
+
+    if (role) {
+      sendNewUserEmail({ email, role })
+    } else {
+      sendSignupEmail({ email, baseUrl, user })
+    }
+  },
+}
+
+const sendNewUserEmail = ({ email, role }) => {
+  email.content.subject = 'Confirm Your Account'
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType: 'user-added-by-admin',
+      role,
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
+
+const sendSignupEmail = ({ email, baseUrl, user }) => {
+  email.content.subject = 'Confirm Your Email Address'
+  email.content.ctaLink = services.createUrl(baseUrl, confirmSignUp, {
+    userId: user.id,
+    confirmationToken: user.confirmationToken,
+  })
+  email.content.ctaText = 'CONFIRM'
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType: 'user-signup',
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
diff --git a/packages/component-email/src/routes/emails/post.js b/packages/component-email/src/routes/emails/post.js
index 33a149738109b8df694383170469a20e83bfc25e..bb7b30d36cd62189b68cca80c9cf5df08be1cda8 100644
--- a/packages/component-email/src/routes/emails/post.js
+++ b/packages/component-email/src/routes/emails/post.js
@@ -1,36 +1,68 @@
 const logger = require('@pubsweet/logger')
-const helpers = require('../../helpers/helpers')
-const emailHelper = require('../../helpers/Email')
+
+const { services } = require('pubsweet-component-helper-service')
+
+const notifications = require('./notifications')
 
 module.exports = models => async (req, res) => {
   const { email, type, role = 'author' } = req.body
-  if (!helpers.checkForUndefinedParams(email, type, role)) {
+  if (!services.checkForUndefinedParams(email, type, role)) {
     res.status(400).json({ error: 'Email and type are required.' })
     logger.error('User ID and role are missing')
     return
   }
 
-  // if (type !== 'invite')
-  //   return res.status(400).json({ error: `Email type ${type} is not defined.` })
+  if (!['signup', 'invite'].includes(type)) {
+    return res.status(400).json({ error: `Email type ${type} is not defined.` })
+  }
+
+  const UserModel = models.User
+
+  try {
+    const user = await UserModel.findByEmail(email)
 
-  if (type === 'signup') {
-    return emailHelper.sendSignupEmail({
-      res,
-      email,
+    if (type === 'signup') {
+      if (!user.confirmationToken) {
+        return res
+          .status(400)
+          .json({ error: 'User does not have a confirmation token.' })
+      }
+    }
+
+    let emailRole
+    if (type === 'invite') {
+      switch (role) {
+        case 'handlingEditor':
+          emailRole = 'Handling Editor'
+          break
+        case 'editorInChief':
+          emailRole = 'Editor in Chief'
+          break
+        case 'admin':
+          emailRole = 'Administrator'
+          break
+        case 'author':
+          emailRole = 'Author'
+          break
+        default:
+          return res.status(400).json({
+            error: `Role ${role} is not defined.`,
+          })
+      }
+    }
+
+    notifications.sendNotifications({
+      user,
+      role: emailRole,
       UserModel: models.User,
-      dashboardUrl: `${req.protocol}://${req.get('host')}`,
+      baseUrl: services.getBaseUrl(req),
     })
-  }
 
-  if (type === 'invite') {
-    return emailHelper.setupNewUserEmail({
-      dashboardUrl: `${req.protocol}://${req.get('host')}`,
-      res,
-      email,
-      role,
-      UserModel: models.User,
+    return res.status(200).json({})
+  } catch (e) {
+    const notFoundError = await services.handleNotFoundError(e, 'User')
+    return res.status(notFoundError.status).json({
+      error: notFoundError.message,
     })
   }
-
-  return res.end()
 }
diff --git a/packages/component-fixture-manager/src/mocks/User.js b/packages/component-fixture-manager/src/mocks/User.js
index 9a7459fc5578d3aa1d5dcec8562fa8f17225b1eb..d9349d696df8914a8e474c9620641a47cd3dc36f 100644
--- a/packages/component-fixture-manager/src/mocks/User.js
+++ b/packages/component-fixture-manager/src/mocks/User.js
@@ -1,4 +1,5 @@
 /* eslint-disable func-names-any */
+const { reviewer } = require('../fixtures/users')
 
 function User(properties) {
   this.type = 'user'
@@ -11,10 +12,17 @@ function User(properties) {
   this.firstName = properties.firstName
   this.lastName = properties.lastName
   this.admin = properties.admin
+  this.isActive = true
+  this.notifications = {
+    email: {
+      user: true,
+      system: true,
+    },
+  }
 }
 
 User.prototype.save = jest.fn(function saveUser() {
-  this.id = '111222'
+  this.id = reviewer.id
   return Promise.resolve(this)
 })
 
diff --git a/packages/component-helper-service/package.json b/packages/component-helper-service/package.json
index e4aa40e13b33df0be7f7a47e1c00a8ef7019a8b1..5764bac137e9af7f48e1736f819e2497d3bd8d62 100644
--- a/packages/component-helper-service/package.json
+++ b/packages/component-helper-service/package.json
@@ -17,7 +17,8 @@
   },
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
-    "pubsweet-server": "^1.0.1"
+    "pubsweet-server": "^1.0.1",
+    "@pubsweet/component-send-email": "0.2.4"
   },
   "publishConfig": {
     "access": "public"
diff --git a/packages/component-helper-service/src/Helper.js b/packages/component-helper-service/src/Helper.js
index 0037b50c55ed4e8f5af9baccf34702df12b77731..b47dc41778f2baec3e50de9a08d72f1b5b4ffc4b 100644
--- a/packages/component-helper-service/src/Helper.js
+++ b/packages/component-helper-service/src/Helper.js
@@ -1,4 +1,4 @@
-const Email = require('./services/Email')
+const Email = require('./services/email/Email')
 const Collection = require('./services/Collection')
 const Fragment = require('./services/Fragment')
 const services = require('./services/services')
diff --git a/packages/component-helper-service/src/services/Collection.js b/packages/component-helper-service/src/services/Collection.js
index dc3513909fd9a9efb99398080319d7ce0b091bd8..5007bba300d33ea17fe4190d2412955c9719c2c0 100644
--- a/packages/component-helper-service/src/services/Collection.js
+++ b/packages/component-helper-service/src/services/Collection.js
@@ -80,6 +80,23 @@ class Collection {
     if (reviewerInvitations.length === 0)
       await this.updateStatus({ newStatus: 'heAssigned' })
   }
+
+  async updateStatusOnRecommendation({ isEditorInChief, recommendation }) {
+    if (isEditorInChief) {
+      if (recommendation === 'return-to-handling-editor') {
+        this.updateStatus({ newStatus: 'reviewCompleted' })
+      } else {
+        this.updateFinalStatusByRecommendation({
+          recommendation,
+        })
+      }
+    } else {
+      this.updateStatusByRecommendation({
+        recommendation,
+        isHandlingEditor: true,
+      })
+    }
+  }
 }
 
 module.exports = Collection
diff --git a/packages/component-helper-service/src/services/Email.js b/packages/component-helper-service/src/services/Email.js
deleted file mode 100644
index d0d815780901df3201eb69f9e0192f6a7274ec9f..0000000000000000000000000000000000000000
--- a/packages/component-helper-service/src/services/Email.js
+++ /dev/null
@@ -1,504 +0,0 @@
-const Fragment = require('./Fragment')
-const User = require('./User')
-const get = require('lodash/get')
-const config = require('config')
-const mailService = require('pubsweet-component-mail-service')
-
-const manuscriptTypes = config.get('manuscript-types')
-
-class Email {
-  constructor({
-    baseUrl,
-    authors = {},
-    UserModel = {},
-    collection = {},
-    parsedFragment = {},
-  }) {
-    this.baseUrl = baseUrl
-    this.authors = authors
-    this.UserModel = UserModel
-    this.collection = collection
-    this.parsedFragment = parsedFragment
-  }
-
-  set _fragment(newFragment) {
-    this.parsedFragment = newFragment
-  }
-
-  static hasReview(invUserId) {
-    return rec =>
-      rec.recommendationType === 'review' &&
-      rec.submittedOn &&
-      invUserId === rec.userId
-  }
-
-  async setupReviewersEmail({
-    FragmentModel,
-    agree = false,
-    isSubmitted = false,
-    recommendation = {},
-  }) {
-    const {
-      baseUrl,
-      UserModel,
-      collection,
-      parsedFragment: { recommendations, title, type },
-      authors: { submittingAuthor: { firstName = '', lastName = '' } },
-    } = this
-    const { parsedFragment: { id } } = this
-    const fragment = await FragmentModel.find(id)
-    const fragmentHelper = new Fragment({ fragment })
-    const reviewerInvitations = fragmentHelper.getReviewerInvitations({
-      agree,
-    })
-
-    const reviewerPromises = await reviewerInvitations.map(async inv => {
-      if (!agree) return UserModel.find(inv.userId)
-      const submittedReview = recommendations.find(
-        this.constructor.hasReview(inv.userId),
-      )
-      const shouldReturnUser =
-        (isSubmitted && submittedReview) || (!isSubmitted && !submittedReview)
-      if (shouldReturnUser) return UserModel.find(inv.userId)
-    })
-
-    let emailType = 'agreed-reviewers-after-recommendation'
-    let emailText, subject, manuscriptType
-
-    const userHelper = new User({ UserModel })
-    const eic = await userHelper.getEditorInChief()
-    const editorName = isSubmitted
-      ? `${eic.firstName} ${eic.lastName}`
-      : collection.handlingEditor.name
-
-    const reviewers = (await Promise.all(reviewerPromises))
-      .filter(Boolean)
-      .filter(rev => rev.isActive)
-      .filter(rev => get(rev, 'notifications.email.user'))
-
-    if (agree) {
-      subject = isSubmitted
-        ? `${collection.customId}: Manuscript Decision`
-        : `${collection.customId}: Manuscript ${getSubject(recommendation)}`
-
-      if (isSubmitted) {
-        emailType = 'submitting-reviewers-after-decision'
-        emailText = 'has now been rejected'
-        if (recommendation === 'publish') emailText = 'will now be published'
-      }
-    } else {
-      subject = `${collection.customId}: Reviewer Unassigned`
-      manuscriptType = manuscriptTypes[type]
-      emailType = 'no-response-reviewers-after-recommendation'
-    }
-
-    reviewers.forEach(user =>
-      mailService.sendNotificationEmail({
-        user,
-        emailType,
-        toId: user.id,
-        toEmail: user.email,
-        meta: {
-          baseUrl,
-          emailText,
-          editorName,
-          collection,
-          manuscriptType,
-          timestamp: Date.now(),
-          emailSubject: subject,
-          reviewerName: `${user.firstName} ${user.lastName}`,
-          fragment: {
-            title,
-            authorName: `${firstName} ${lastName}`,
-            id,
-          },
-        },
-      }),
-    )
-  }
-
-  async sendNewVersionSubmittedReviewersEmail({
-    newFragmentId = '',
-    invitations = [],
-  }) {
-    const {
-      baseUrl,
-      UserModel,
-      collection,
-      parsedFragment: { title },
-      authors: { submittingAuthor: { firstName = '', lastName = '' } },
-    } = this
-    ;(await Promise.all(invitations.map(inv => UserModel.find(inv.userId))))
-      .filter(rev => rev.isActive)
-      .filter(rev => get(rev, 'notifications.email.user'))
-      .forEach(user =>
-        mailService.sendNotificationEmail({
-          emailType: 'submitting-reviewers-after-revision',
-          toId: user.id,
-          toEmail: user.email,
-          meta: {
-            baseUrl,
-            collection,
-            timestamp: Date.now(),
-            reviewerName: `${user.firstName} ${user.lastName}`,
-            fragment: {
-              title,
-              authorName: `${firstName} ${lastName}`,
-              id: newFragmentId,
-            },
-          },
-        }),
-      )
-  }
-
-  async setupAuthorsEmail({
-    requestToRevision = false,
-    publish = false,
-    FragmentModel,
-  }) {
-    const {
-      baseUrl,
-      UserModel,
-      collection,
-      parsedFragment: { heRecommendation, id, title, newComments },
-      authors: {
-        submittingAuthor: {
-          id: submittingAuthorId,
-          email,
-          firstName,
-          lastName,
-        },
-      },
-    } = this
-    let comments = get(heRecommendation, 'comments', [])
-    if (requestToRevision) comments = newComments
-    const authorNote = comments.find(comm => comm.public)
-    const content = get(authorNote, 'content')
-    const authorNoteText = content ? `Reason & Details: "${content}"` : ''
-    let emailType = requestToRevision
-      ? 'author-request-to-revision'
-      : 'author-manuscript-rejected'
-    if (publish) emailType = 'author-manuscript-published'
-    let toAuthors = null
-    if (emailType === 'author-request-to-revision') {
-      toAuthors = [
-        {
-          id: submittingAuthorId,
-          email,
-          name: `${firstName} ${lastName}`,
-        },
-      ]
-    } else {
-      const fragment = await FragmentModel.find(id)
-      const userHelper = new User({ UserModel })
-      const activeAuthors = await userHelper.getActiveAuthors(fragment.authors)
-
-      toAuthors = activeAuthors.map(author => ({
-        id: author.id,
-        email: author.email,
-        name: `${author.firstName} ${author.lastName}`,
-      }))
-    }
-    toAuthors.forEach(toAuthor => {
-      mailService.sendNotificationEmail({
-        emailType,
-        toId: toAuthor.id,
-        toEmail: toAuthor.email,
-        meta: {
-          handlingEditorName: get(collection, 'handlingEditor.name'),
-          baseUrl,
-          collection,
-          authorNoteText,
-          fragment: {
-            id,
-            title,
-            authorName: toAuthor.name,
-            submittingAuthorName: `${firstName} ${lastName}`,
-          },
-        },
-      })
-    })
-  }
-
-  async setupHandlingEditorEmail({
-    publish = false,
-    returnWithComments = false,
-    reviewSubmitted = false,
-    reviewerName = '',
-  }) {
-    const {
-      baseUrl,
-      UserModel,
-      collection,
-      parsedFragment: { eicComments = '', title, id },
-      authors: { submittingAuthor: { firstName = '', lastName = '' } },
-    } = this
-    const user = get(collection, 'handlingEditor', {})
-    if (!get(user, 'notifications.email.user')) return
-
-    const userHelper = new User({ UserModel })
-    const eic = await userHelper.getEditorInChief()
-    let emailType = publish
-      ? 'he-manuscript-published'
-      : 'he-manuscript-rejected'
-    if (reviewSubmitted) emailType = 'review-submitted'
-    if (returnWithComments) emailType = 'he-manuscript-return-with-comments'
-
-    mailService.sendNotificationEmail({
-      user,
-      toId: user.id,
-      toEmail: user.email,
-      emailType,
-      meta: {
-        baseUrl,
-        collection,
-        reviewerName,
-        eicComments,
-        eicName: `${eic.firstName} ${eic.lastName}`,
-        emailSubject: `${collection.customId}: Manuscript Decision`,
-        handlingEditorName: user.name || '',
-        fragment: {
-          id,
-          title,
-          authorName: `${firstName} ${lastName}`,
-        },
-      },
-    })
-  }
-
-  async setupEiCEmail({ recommendation, comments }) {
-    const {
-      baseUrl,
-      UserModel,
-      collection,
-      parsedFragment: { title, id },
-      authors: { submittingAuthor: { firstName, lastName } },
-    } = this
-    const userHelper = new User({ UserModel })
-    const eic = await userHelper.getEditorInChief()
-    if (!get(eic, 'notifications.email.user')) return
-
-    const privateNote = comments.find(comm => comm.public === false)
-    const content = get(privateNote, 'content')
-    const privateNoteText =
-      content !== undefined ? `Private note: "${content}"` : ''
-    let paragraph
-    const heRecommendation = getHeRecommendation(recommendation)
-    const manuscriptAuthorText = `the manuscript titled "${title}" by ${firstName} ${lastName}`
-    const publishOrRejectText =
-      'It is my recommendation, based on the reviews I have received for'
-    switch (heRecommendation) {
-      case 'publish':
-        paragraph = `${publishOrRejectText} ${manuscriptAuthorText}, that we should proceed to publication. <br/><br/>
-        ${privateNoteText}<br/><br/>`
-        break
-      case 'reject':
-        paragraph = `${publishOrRejectText}
-        ${manuscriptAuthorText}, that we should reject it for publication. <br/><br/>
-        ${privateNoteText}<br/><br/>`
-        break
-      case 'revision':
-        paragraph = `In order for ${manuscriptAuthorText} to proceed to publication, there needs to be a revision. <br/><br/>
-        ${privateNoteText}<br/><br/>`
-        break
-
-      default:
-        throw new Error('undefined HE recommentation type')
-    }
-
-    mailService.sendNotificationEmail({
-      toId: eic.id,
-      toEmail: eic.email,
-      emailType: 'eic-recommendation',
-      meta: {
-        baseUrl,
-        paragraph,
-        collection,
-        fragment: { id },
-        eicName: `${eic.firstName} ${eic.lastName}`,
-        handlingEditorName: get(collection, 'handlingEditor.name'),
-      },
-    })
-  }
-
-  setupReviewerInvitationEmail({
-    user,
-    invitationId,
-    timestamp,
-    resend = false,
-    authorName,
-  }) {
-    const {
-      baseUrl,
-      collection,
-      parsedFragment: { id, title, abstract },
-      authors,
-    } = this
-
-    if (!get(user, 'notifications.email.user')) return
-
-    const params = {
-      user,
-      baseUrl,
-      subject: `${collection.customId}: Review Requested`,
-      toEmail: user.email,
-      meta: {
-        fragment: {
-          id,
-          title,
-          abstract,
-          authors,
-        },
-        invitation: {
-          id: invitationId,
-          timestamp,
-        },
-        collection: {
-          id: collection.id,
-          authorName,
-          handlingEditor: collection.handlingEditor,
-        },
-      },
-      emailType: resend ? 'resend-reviewer' : 'invite-reviewer',
-    }
-    mailService.sendReviewerInvitationEmail(params)
-  }
-
-  async setupReviewerDecisionEmail({
-    user,
-    agree = false,
-    timestamp = Date.now(),
-    authorName = '',
-  }) {
-    const {
-      baseUrl,
-      UserModel,
-      collection,
-      parsedFragment: { id, title },
-    } = this
-    const handlingEditor = get(collection, 'handlingEditor')
-
-    if (!get(handlingEditor, 'notifications.email.user', true)) return
-
-    const userHelper = new User({ UserModel })
-    const eic = await userHelper.getEditorInChief()
-
-    mailService.sendNotificationEmail({
-      user,
-      toId: handlingEditor.id,
-      toEmail: handlingEditor.email,
-      emailType: agree ? 'reviewer-agreed' : 'reviewer-declined',
-      meta: {
-        collection: { customId: collection.customId, id: collection.id },
-        fragment: { id, title, authorName },
-        handlingEditorName: collection.handlingEditor.name,
-        baseUrl,
-        eicName: `${eic.firstName} ${eic.lastName}`,
-        timestamp,
-      },
-    })
-    if (agree)
-      mailService.sendNotificationEmail({
-        user,
-        toId: user.id,
-        toEmail: user.email,
-        emailType: 'reviewer-thank-you',
-        meta: {
-          collection: { customId: collection.customId, id: collection.id },
-          fragment: { id, title, authorName },
-          handlingEditorName: collection.handlingEditor.name,
-          baseUrl,
-        },
-      })
-  }
-
-  async setupReviewerUnassignEmail({ user, authorName }) {
-    const { collection, parsedFragment: { title = '' } } = this
-    if (!get(user, 'notifications.email.user')) return
-
-    await mailService.sendNotificationEmail({
-      user,
-      toId: user.id,
-      toEmail: user.email,
-      emailType: 'unassign-reviewer',
-      meta: {
-        collection: { customId: collection.customId },
-        fragment: { title, authorName },
-        handlingEditorName: collection.handlingEditor.name,
-      },
-    })
-  }
-
-  async setupManuscriptSubmittedEmail() {
-    const {
-      baseUrl,
-      UserModel,
-      collection,
-      parsedFragment: { id, title },
-      authors: { submittingAuthor: { firstName = '', lastName = '' } },
-    } = this
-
-    const userHelper = new User({ UserModel })
-    const eic = await userHelper.getEditorInChief()
-
-    if (!get(eic, 'notifications.email.user')) return
-
-    mailService.sendSimpleEmail({
-      user: eic,
-      toId: eic.id,
-      toEmail: eic.email,
-      emailType: 'manuscript-submitted',
-      dashboardUrl: baseUrl,
-      meta: {
-        collection,
-        fragment: { id, authorName: `${firstName} ${lastName}`, title },
-        eicName: `${eic.firstName} ${eic.lastName}`,
-      },
-    })
-  }
-
-  async setupNewVersionSubmittedEmail() {
-    const {
-      baseUrl,
-      UserModel,
-      collection,
-      parsedFragment: { id, title },
-      authors: { submittingAuthor: { firstName = '', lastName = '' } },
-    } = this
-    const handlingEditor = get(collection, 'handlingEditor', {})
-    const user = await UserModel.find(handlingEditor.id)
-    if (!get(user, 'notifications.email.user')) return
-
-    const userHelper = new User({ UserModel })
-    const eic = await userHelper.getEditorInChief()
-
-    mailService.sendNotificationEmail({
-      user,
-      emailType: 'new-version-submitted',
-      toId: user.id,
-      toEmail: user.email,
-      meta: {
-        baseUrl,
-        collection,
-        eicName: `${eic.firstName} ${eic.lastName}`,
-        fragment: { id, authorName: `${firstName} ${lastName}`, title },
-        handlingEditorName: handlingEditor.name || '',
-      },
-    })
-  }
-}
-
-const getSubject = recommendation =>
-  ['minor', 'major'].includes(recommendation)
-    ? 'Revision Requested'
-    : 'Recommendation Submitted'
-
-const getHeRecommendation = recommendation => {
-  let heRecommendation = recommendation === 'reject' ? 'reject' : 'publish'
-  if (['minor', 'major'].includes(recommendation)) {
-    heRecommendation = 'revision'
-  }
-  return heRecommendation
-}
-
-module.exports = Email
diff --git a/packages/component-helper-service/src/services/Fragment.js b/packages/component-helper-service/src/services/Fragment.js
index 15267b2d0349ac73ed0ecc4d28126fedcc7ac32b..a0e8494bb014ebe42a81ee538f68351d5cfa53ca 100644
--- a/packages/component-helper-service/src/services/Fragment.js
+++ b/packages/component-helper-service/src/services/Fragment.js
@@ -21,11 +21,16 @@ class Fragment {
     return owners
   }
 
-  async getFragmentData({ handlingEditor = {} }) {
+  async getFragmentData({ handlingEditor } = {}) {
     const { fragment: { metadata = {}, recommendations = [], id } } = this
-    const heRecommendation = recommendations.find(
-      rec => rec.userId === handlingEditor.id,
-    )
+    let heRecommendation
+
+    if (handlingEditor) {
+      heRecommendation = recommendations.find(
+        rec => rec.userId === handlingEditor.id,
+      )
+    }
+
     let { title = '', abstract = '' } = metadata
     const { type } = metadata
     title = title.replace(/<(.|\n)*?>/g, '')
@@ -73,12 +78,8 @@ class Fragment {
       const userHelper = new User({ UserModel })
       const activeAuthors = await userHelper.getActiveAuthors(authors)
 
-      const authorsList = activeAuthors.map(
-        author => `${author.firstName} ${author.lastName}`,
-      )
-
       return {
-        authorsList,
+        activeAuthors,
         submittingAuthor,
       }
     } catch (e) {
@@ -86,18 +87,36 @@ class Fragment {
     }
   }
 
-  getReviewerInvitations({ agree = true }) {
-    const { fragment: { invitations = [] } } = this
-    return agree
+  getInvitations({ isAccepted = true, role = 'reviewer', type }) {
+    const { fragment: { invitations = [], recommendations = [] } } = this
+    let filteredInvitations = isAccepted
       ? invitations.filter(
           inv =>
-            inv.role === 'reviewer' &&
+            inv.role === role &&
             inv.hasAnswer === true &&
             inv.isAccepted === true,
         )
-      : invitations.filter(
-          inv => inv.role === 'reviewer' && inv.hasAnswer === false,
-        )
+      : invitations.filter(inv => inv.role === role && inv.hasAnswer === false)
+
+    if (type === 'submitted') {
+      filteredInvitations = filteredInvitations.filter(inv =>
+        recommendations.find(
+          rec =>
+            rec.recommendationType === 'review' &&
+            rec.submittedOn &&
+            inv.userId === rec.userId,
+        ),
+      )
+    } else if (type === 'accepted') {
+      filteredInvitations = filteredInvitations.filter(inv =>
+        recommendations.find(
+          rec =>
+            rec.recommendationType === 'review' && inv.userId !== rec.userId,
+        ),
+      )
+    }
+
+    return filteredInvitations
   }
 
   getLatestHERequestToRevision() {
@@ -111,20 +130,15 @@ class Fragment {
       .sort((a, b) => b.createdOn - a.createdOn)[0]
   }
 
-  async getInvitationsForSubmittingReviewers() {
-    const { fragment: { recommendations = [] } } = this
-    const agreedInvitations = this.getReviewerInvitations({
-      agree: true,
-    })
+  async getReviewers({ UserModel, type }) {
+    const isAccepted = type !== 'pending'
+    const invitations = await this.getInvitations({ isAccepted, type })
 
-    return agreedInvitations.filter(inv =>
-      recommendations.find(
-        rec =>
-          rec.recommendationType === 'review' &&
-          rec.submittedOn &&
-          inv.userId === rec.userId,
-      ),
-    )
+    return (await Promise.all(
+      invitations.map(inv => UserModel.find(inv.userId)),
+    ))
+      .filter(rev => rev.isActive)
+      .filter(rev => get(rev, 'notifications.email.user'))
   }
 }
 
diff --git a/packages/component-helper-service/src/services/User.js b/packages/component-helper-service/src/services/User.js
index 633de7eb1e2c23a17385a049b7d29f8da764615e..074414d59f43dcf979d912244249fce897b7d7b7 100644
--- a/packages/component-helper-service/src/services/User.js
+++ b/packages/component-helper-service/src/services/User.js
@@ -1,8 +1,6 @@
 const uuid = require('uuid')
 const { get } = require('lodash')
 const crypto = require('crypto')
-const logger = require('@pubsweet/logger')
-const mailService = require('pubsweet-component-mail-service')
 
 class User {
   constructor({ UserModel = {} }) {
@@ -43,30 +41,11 @@ class User {
     return newUser
   }
 
-  async setupNewUser({ url, role, invitationType, body = {} }) {
-    const newUser = await this.createUser({ role, body })
-
-    try {
-      if (role !== 'reviewer') {
-        mailService.sendSimpleEmail({
-          toEmail: newUser.email,
-          user: newUser,
-          emailType: invitationType,
-          dashboardUrl: url,
-        })
-      }
-
-      return newUser
-    } catch (e) {
-      logger.error(e.message)
-      return { status: 500, error: 'Email could not be sent.' }
-    }
-  }
-
   async getEditorInChief() {
     const { UserModel } = this
     const users = await UserModel.all()
     const eic = users.find(user => user.editorInChief || user.admin)
+
     return eic
   }
 
diff --git a/packages/component-helper-service/src/services/email/Email.js b/packages/component-helper-service/src/services/email/Email.js
new file mode 100644
index 0000000000000000000000000000000000000000..8d5b688b6993b3ebb9df7ab25630b926aa7762c6
--- /dev/null
+++ b/packages/component-helper-service/src/services/email/Email.js
@@ -0,0 +1,79 @@
+const config = require('config')
+const helpers = require('./helpers')
+const SendEmail = require('@pubsweet/component-send-email')
+
+class Email {
+  constructor({
+    type = 'system',
+    toUser = {
+      id: '',
+      email: '',
+      name: '',
+    },
+    content = {
+      subject: '',
+      ctaLink: '',
+      ctaText: '',
+      signatureName: '',
+      unsubscribeLink: '',
+    },
+  }) {
+    this.type = type
+    this.toUser = toUser
+    this.content = content
+  }
+
+  set _toUser(newToUser) {
+    this.toUser = newToUser
+  }
+
+  set _subject(newSubject) {
+    this.subject = newSubject
+  }
+
+  set _content(newContent) {
+    this.content = newContent
+  }
+
+  getBody({ body = {}, isReviewerInvitation = false }) {
+    if (isReviewerInvitation) {
+      return {
+        html: helpers.getInvitationBody({
+          replacements: {
+            toUserName: this.toUser.name,
+            ...this.content,
+            ...body,
+          },
+        }),
+        text: `${this.content.signatureName}`,
+      }
+    }
+
+    return {
+      html: helpers.getNotificationBody({
+        replacements: {
+          ...body,
+          toUserName: this.toUser.name,
+          ...this.content,
+        },
+      }),
+      text: `${body.paragraph} ${this.content.ctaLink} ${
+        this.content.ctaText
+      } ${this.content.signatureName}`,
+    }
+  }
+
+  sendEmail({ text, html }) {
+    const mailData = {
+      from: config.get('mailer.from'),
+      to: this.toUser.email,
+      subject: this.content.subject,
+      text,
+      html,
+    }
+
+    SendEmail.send(mailData)
+  }
+}
+
+module.exports = Email
diff --git a/packages/component-helper-service/src/services/email/helpers.js b/packages/component-helper-service/src/services/email/helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..4a8559e128c3de4b020f910fc8a55022eba598a1
--- /dev/null
+++ b/packages/component-helper-service/src/services/email/helpers.js
@@ -0,0 +1,53 @@
+// const querystring = require('querystring')
+const fs = require('fs')
+const handlebars = require('handlebars')
+
+const getNotificationBody = ({ replacements }) => {
+  handlePartial('header', replacements)
+  handlePartial('footer', replacements)
+  handlePartial('signature', replacements)
+  if (replacements.hasLink) handlePartial('button', replacements)
+  handlePartial('body', replacements)
+
+  return getMainTemplate({ fileName: 'notification', context: replacements })
+}
+
+const getInvitationBody = ({ replacements }) => {
+  handlePartial('invHeader', replacements)
+  handlePartial('footer', replacements)
+  handlePartial('invUpperContent', replacements)
+  handlePartial('invButtons', replacements)
+  handlePartial('invManuscriptData', replacements)
+  handlePartial('signature', replacements)
+  handlePartial('invLowerContent', replacements)
+
+  return getMainTemplate({ fileName: 'invitation', context: replacements })
+}
+
+const readFile = path =>
+  fs.readFileSync(path, { encoding: 'utf-8' }, (err, file) => {
+    if (err) {
+      throw err
+    } else {
+      return file
+    }
+  })
+
+const handlePartial = (partialName = 'signature', context = {}) => {
+  let partial = readFile(`${__dirname}/templates/partials/${partialName}.hbs`)
+  const template = handlebars.compile(partial)
+  partial = template(context)
+  handlebars.registerPartial(partialName, partial)
+}
+
+const getMainTemplate = ({ fileName, context }) => {
+  const htmlFile = readFile(`${__dirname}/templates/${fileName}.html`)
+  const htmlTemplate = handlebars.compile(htmlFile)
+  const htmlBody = htmlTemplate(context)
+  return htmlBody
+}
+
+module.exports = {
+  getNotificationBody,
+  getInvitationBody,
+}
diff --git a/packages/component-helper-service/src/services/email/templates/invitation.html b/packages/component-helper-service/src/services/email/templates/invitation.html
new file mode 100644
index 0000000000000000000000000000000000000000..f9d5ed18cfa96454ba3454e9555e6144a6411467
--- /dev/null
+++ b/packages/component-helper-service/src/services/email/templates/invitation.html
@@ -0,0 +1,5 @@
+{{> invHeader }}
+{{> invUpperContent }}
+{{> invButtons }}
+{{> invLowerContent }}
+{{> footer }}
\ No newline at end of file
diff --git a/packages/component-helper-service/src/services/email/templates/notification.html b/packages/component-helper-service/src/services/email/templates/notification.html
new file mode 100644
index 0000000000000000000000000000000000000000..02cbf2a6e9c1b383534faf9358c0df3cd078c534
--- /dev/null
+++ b/packages/component-helper-service/src/services/email/templates/notification.html
@@ -0,0 +1,3 @@
+{{> header }}
+{{> body }}
+{{> footer}}
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/partials/notificationBody.hbs b/packages/component-helper-service/src/services/email/templates/partials/body.hbs
similarity index 81%
rename from packages/component-mail-service/src/templates/partials/notificationBody.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/body.hbs
index c041158eada8ebec8a422962b28f849a647042d2..99513b980cc51a71d84c3d0b1d403f5a9e303679 100644
--- a/packages/component-mail-service/src/templates/partials/notificationBody.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/body.hbs
@@ -2,19 +2,20 @@
   <tr>
     <td style="padding:30px 23px 0px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff">
       <div>
-        <p data-pm-slice="1 1 []">{{intro}},</p>
+        <p data-pm-slice="1 1 []">Dear Dr. {{toUserName}},</p>
         <p>&nbsp;</p>
         <p>
           {{{paragraph}}}
+        </p>
+        <p>&nbsp;</p>
+        <p>
           {{#if hasLink }}
-            {{> manuscriptDetailsLink}}
+            {{> button }}
           {{/if}}
         </p>
-        <p>&nbsp;</p>
         {{> signature}}
         <p>&nbsp;</p>
       </div>
-
     </td>
   </tr>
 </table>
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/partials/mainButton.hbs b/packages/component-helper-service/src/services/email/templates/partials/button.hbs
similarity index 64%
rename from packages/component-mail-service/src/templates/partials/mainButton.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/button.hbs
index 9833fff520dd7a266851ca078aed1dbe899874f8..3b78683c484d20b9bdfcf72d7b68e9490ba04230 100644
--- a/packages/component-mail-service/src/templates/partials/mainButton.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/button.hbs
@@ -7,8 +7,8 @@
           <tbody>
             <tr>
               <td align="center" bgcolor="#0d78f2" class="inner-td" style="border-radius:6px;font-size:16px;text-align:center;background-color:inherit">
-                <a href="{{ url }}" style="background-color:#0d78f2;border:1px solid #333333;border-color:#0d78f2;border-radius:0px;border-width:1px;color:#ffffff;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none"
-                  target="_blank">{{ buttonText }}</a>
+                <a href="{{ ctaLink }}" style="background-color:#0d78f2;border:1px solid #333333;border-color:#0d78f2;border-radius:0px;border-width:1px;color:#ffffff;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none"
+                  target="_blank">{{ ctaText }}</a>
               </td>
             </tr>
           </tbody>
diff --git a/packages/component-mail-service/src/templates/partials/footer.hbs b/packages/component-helper-service/src/services/email/templates/partials/footer.hbs
similarity index 100%
rename from packages/component-mail-service/src/templates/partials/footer.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/footer.hbs
diff --git a/packages/component-mail-service/src/templates/partials/header.hbs b/packages/component-helper-service/src/services/email/templates/partials/header.hbs
similarity index 98%
rename from packages/component-mail-service/src/templates/partials/header.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/header.hbs
index 3e8efa3eaeff420f3bbf0e68bc88d48713410851..a62d4be742baba10db155a84f6db8e6535880c7b 100644
--- a/packages/component-mail-service/src/templates/partials/header.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/header.hbs
@@ -145,7 +145,7 @@
                                 width="100%" style="display: none !important; mso-hide: all; visibility: hidden; opacity: 0; color: transparent; height: 0; width: 0;">
                                 <tr>
                                   <td role="module-content">
-                                    <p>{{ previewText }}</p>
+                                    <p>you have a new notification</p>
                                   </td>
                                 </tr>
                               </table>
diff --git a/packages/component-mail-service/src/templates/partials/invitationButtons.hbs b/packages/component-helper-service/src/services/email/templates/partials/invButtons.hbs
similarity index 96%
rename from packages/component-mail-service/src/templates/partials/invitationButtons.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/invButtons.hbs
index 002013ea2b7fa1e76bf6441fed608f79d0a8c99b..5c5041dec3a973c019e5743106826fa11137c14e 100644
--- a/packages/component-mail-service/src/templates/partials/invitationButtons.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/invButtons.hbs
@@ -27,7 +27,7 @@
                         <tr>
                           <td align="center" bgcolor="#0d78f2" class="inner-td" style="border-radius:6px;font-size:16px;text-align:center;background-color:inherit">
                             <a style="background-color:#0d78f2;border:1px solid #333333;border-color:#0d78f2;border-radius:0px;border-width:1px;color:#ffffff;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none"
-                              href="{{agreeUrl}}" target="_blank">AGREE</a>
+                              href="{{ agreeLink }}" target="_blank">AGREE</a>
                           </td>
                         </tr>
                       </tbody>
@@ -62,7 +62,7 @@
                         <tr>
                           <td align="center" bgcolor="#e4dfdf" class="inner-td" style="border-radius:6px;font-size:16px;text-align:center;background-color:inherit">
                             <a style="background-color:#e4dfdf;border:1px solid #333333;border-color:#E4DFDF;border-radius:0px;border-width:1px;color:#302e2e;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none"
-                              href="{{declineUrl}}" target="_blank">DECLINE</a>
+                              href="{{ declineLink }}" target="_blank">DECLINE</a>
                           </td>
                         </tr>
                       </tbody>
diff --git a/packages/component-mail-service/src/templates/partials/invitationHeader.hbs b/packages/component-helper-service/src/services/email/templates/partials/invHeader.hbs
similarity index 98%
rename from packages/component-mail-service/src/templates/partials/invitationHeader.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/invHeader.hbs
index 3e8efa3eaeff420f3bbf0e68bc88d48713410851..a8390f81f0389272c8824e79e17fda96b444748d 100644
--- a/packages/component-mail-service/src/templates/partials/invitationHeader.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/invHeader.hbs
@@ -145,7 +145,7 @@
                                 width="100%" style="display: none !important; mso-hide: all; visibility: hidden; opacity: 0; color: transparent; height: 0; width: 0;">
                                 <tr>
                                   <td role="module-content">
-                                    <p>{{ previewText }}</p>
+                                    <p>new invitation</p>
                                   </td>
                                 </tr>
                               </table>
diff --git a/packages/component-mail-service/src/templates/partials/invitationLowerContent.hbs b/packages/component-helper-service/src/services/email/templates/partials/invLowerContent.hbs
similarity index 84%
rename from packages/component-mail-service/src/templates/partials/invitationLowerContent.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/invLowerContent.hbs
index ef60e6b2aa6d9d303515dfc6feead9c054c30cc6..05a44b844d5fbd75da38c1864a74e42ecdae399e 100644
--- a/packages/component-mail-service/src/templates/partials/invitationLowerContent.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/invLowerContent.hbs
@@ -2,12 +2,12 @@
   <tr>
     <td style="padding:30px 23px 0px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff">
       <p data-pm-slice="1 1 []">
-        <a href="{{ manuscriptDetailsUrl }}">See more information</a>
+        <a href="{{ detailsLink }}">See more information</a>
       </p>
 
       <p data-pm-slice="1 1 []">&nbsp;</p>
 
-      {{> manuscriptData }}
+      {{> invManuscriptData }}
       <p data-pm-slice="1 1 []">{{{ lowerContent }}}</p>
 
       <p>&nbsp;</p>
diff --git a/packages/component-mail-service/src/templates/partials/manuscriptData.hbs b/packages/component-helper-service/src/services/email/templates/partials/invManuscriptData.hbs
similarity index 68%
rename from packages/component-mail-service/src/templates/partials/manuscriptData.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/invManuscriptData.hbs
index 26bb10cc98d9ad17cc93ab99bc462cd63f2f9d50..36d60dbe2943b37e8ef7ba15031256728b8f409b 100644
--- a/packages/component-mail-service/src/templates/partials/manuscriptData.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/invManuscriptData.hbs
@@ -1,9 +1,9 @@
 <p data-pm-slice="1 1 []">{{ manuscriptText }}</p>
 <p>&nbsp;</p>
-<h2>{{title}}</h2>
+<h2>{{ title }}</h2>
 <p>&nbsp;</p>
 <h4>
-  <span style="font-family:arial,helvetica,sans-serif;">{{authors}}</span>
+  <span style="font-family:arial,helvetica,sans-serif;">{{ authorsList }}</span>
 </h4>
 <p>
   <br />
diff --git a/packages/component-mail-service/src/templates/partials/invitationUpperContent.hbs b/packages/component-helper-service/src/services/email/templates/partials/invUpperContent.hbs
similarity index 85%
rename from packages/component-mail-service/src/templates/partials/invitationUpperContent.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/invUpperContent.hbs
index 1bacf2e791241303b5242c62b5f2393a6dc43930..c32a513a9541d234ae3d5ed01bcae96824f728a0 100644
--- a/packages/component-mail-service/src/templates/partials/invitationUpperContent.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/invUpperContent.hbs
@@ -2,7 +2,7 @@
   <tr>
     <td style="padding:30px 23px 20px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff">
       <div>
-        <p data-pm-slice="1 1 []">{{ intro }},</p>
+        <p data-pm-slice="1 1 []">Dear Dr. {{ toUserName }},</p>
 
         <p>&nbsp;</p>
         <p>{{ upperContent }}</p>
diff --git a/packages/component-mail-service/src/templates/partials/signature.hbs b/packages/component-helper-service/src/services/email/templates/partials/signature.hbs
similarity index 74%
rename from packages/component-mail-service/src/templates/partials/signature.hbs
rename to packages/component-helper-service/src/services/email/templates/partials/signature.hbs
index 29cb70c53ef98e4eef1e0fb01dff1eb925320151..615770e162d7915b38e49c485404aa7682f75339 100644
--- a/packages/component-mail-service/src/templates/partials/signature.hbs
+++ b/packages/component-helper-service/src/services/email/templates/partials/signature.hbs
@@ -1,5 +1,4 @@
 <p>With many thanks and best regards,
   <br /> {{ signatureName }}
-  <br /> {{ signatureEmail }}
   <br /> Hindawi
 </p>
\ No newline at end of file
diff --git a/packages/component-helper-service/src/services/services.js b/packages/component-helper-service/src/services/services.js
index cb56e8ec7edd3c811f3f77f11f66567c70d4a898..dab0fae58764405a3b8b4c5add996ee43657fe2b 100644
--- a/packages/component-helper-service/src/services/services.js
+++ b/packages/component-helper-service/src/services/services.js
@@ -1,4 +1,5 @@
 const logger = require('@pubsweet/logger')
+const querystring = require('querystring')
 
 const checkForUndefinedParams = (...params) => {
   if (params.includes(undefined)) {
@@ -75,9 +76,29 @@ const handleNotFoundError = async (error, item) => {
 
 const getBaseUrl = req => `${req.protocol}://${req.get('host')}`
 
+const createUrl = (baseUrl, slug, queryParams = null) =>
+  !queryParams
+    ? `${baseUrl}${slug}`
+    : `${baseUrl}${slug}?${querystring.encode(queryParams)}`
+
+const getExpectedDate = ({ timestamp = Date.now(), daysExpected = 0 }) => {
+  const date = new Date(timestamp)
+  let expectedDate = date.getDate() + daysExpected
+  date.setDate(expectedDate)
+
+  expectedDate = date.toLocaleDateString('en-US', {
+    day: 'numeric',
+    month: 'long',
+    year: 'numeric',
+  })
+
+  return expectedDate
+}
 module.exports = {
   checkForUndefinedParams,
   validateEmailAndToken,
   handleNotFoundError,
   getBaseUrl,
+  createUrl,
+  getExpectedDate,
 }
diff --git a/packages/component-invite/package.json b/packages/component-invite/package.json
index 4851637a00cd63c02aaa8d79af5eb3bcd42d32a6..7dc311684293ed1b767baac50292f67902454255 100644
--- a/packages/component-invite/package.json
+++ b/packages/component-invite/package.json
@@ -24,8 +24,8 @@
   },
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
-    "pubsweet-component-mail-service": "0.0.1",
-    "pubsweet-server": "^1.0.1"
+    "pubsweet-server": "^1.0.1",
+    "@pubsweet/component-send-email": "0.2.4"
   },
   "devDependencies": {
     "apidoc": "^0.17.6",
diff --git a/packages/component-invite/src/routes/collectionsInvitations/delete.js b/packages/component-invite/src/routes/collectionsInvitations/delete.js
index f82dffef10760d85c495c1a895cb995d0dc13a00..5686d03f2ee8df3c62d1e212fea56cc3bc5a46e0 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/delete.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/delete.js
@@ -1,11 +1,11 @@
-const mailService = require('pubsweet-component-mail-service')
-
 const {
-  services,
   Team,
+  services,
   authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
 
+const notifications = require('./emails/notifications')
+
 module.exports = models => async (req, res) => {
   const { collectionId, invitationId } = req.params
   const teamHelper = new Team({ TeamModel: models.Team, collectionId })
@@ -56,9 +56,13 @@ module.exports = models => async (req, res) => {
     user.teams = user.teams.filter(userTeamId => team.id !== userTeamId)
     await user.save()
 
-    mailService.sendSimpleEmail({
-      toEmail: user.email,
-      emailType: 'revoke-handling-editor',
+    notifications.sendNotifications({
+      models,
+      collection,
+      isEiC: true,
+      invitedHE: user,
+      isCanceled: true,
+      baseUrl: services.getBaseUrl(req),
     })
 
     return res.status(200).json({})
diff --git a/packages/component-invite/src/routes/collectionsInvitations/emails/emailCopy.js b/packages/component-invite/src/routes/collectionsInvitations/emails/emailCopy.js
new file mode 100644
index 0000000000000000000000000000000000000000..58d6e89b309ebd7162cc1d484d10534e846d3edf
--- /dev/null
+++ b/packages/component-invite/src/routes/collectionsInvitations/emails/emailCopy.js
@@ -0,0 +1,29 @@
+const getEmailCopy = ({ emailType, titleText, targetUserName, comments }) => {
+  let paragraph
+  let hasLink = true
+  switch (emailType) {
+    case 'he-assigned':
+      paragraph = `You have been assigned as a Handling Editor to ${titleText}. Please click on the link below to access the manuscript and make a decision.`
+      break
+    case 'he-accepted':
+      paragraph = `Dr. ${targetUserName} agreed to be a Handling Editor on ${titleText}. Please click on the link below to access the manuscript.`
+      break
+    case 'he-declined':
+      paragraph = `Dr. ${targetUserName} has declined to be a Handling Editor on ${titleText}.<br/><br/>
+      ${comments}`
+      hasLink = false
+      break
+    case 'he-revoked':
+      paragraph = `Your Handling Editor assignment to ${titleText} has been revoked.`
+      hasLink = false
+      break
+    default:
+      throw new Error(`The ${emailType} email type is not defined.`)
+  }
+
+  return { paragraph, hasLink }
+}
+
+module.exports = {
+  getEmailCopy,
+}
diff --git a/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js b/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js
new file mode 100644
index 0000000000000000000000000000000000000000..44c00f6856cbb40b92f735a8744df41ce73532b7
--- /dev/null
+++ b/packages/component-invite/src/routes/collectionsInvitations/emails/notifications.js
@@ -0,0 +1,144 @@
+const config = require('config')
+const { last } = require('lodash')
+
+const unsubscribeSlug = config.get('unsubscribe.url')
+
+const {
+  User,
+  Email,
+  services,
+  Fragment,
+} = require('pubsweet-component-helper-service')
+
+const { getEmailCopy } = require('./emailCopy')
+
+module.exports = {
+  async sendNotifications({
+    reason,
+    baseUrl,
+    invitedHE,
+    collection,
+    isEiC = false,
+    isCanceled = false,
+    isAccepted = false,
+    models: { User: UserModel, Fragment: FragmentModel },
+  }) {
+    const fragmentId = last(collection.fragments)
+    const fragment = await FragmentModel.find(fragmentId)
+    const fragmentHelper = new Fragment({ fragment })
+    const { title } = await fragmentHelper.getFragmentData()
+    const { submittingAuthor } = await fragmentHelper.getAuthorData({
+      UserModel,
+    })
+
+    const titleText = `the manuscript titled "${title}" by ${
+      submittingAuthor.firstName
+    } ${submittingAuthor.lastName}`
+
+    const userHelper = new User({ UserModel })
+    const eic = await userHelper.getEditorInChief()
+    const eicName = `${eic.firstName} ${eic.lastName}`
+    const subjectBaseText = `${collection.customId}: Manuscript `
+
+    const email = new Email({
+      type: 'user',
+      content: {
+        signatureName: eicName,
+        ctaLink: services.createUrl(
+          baseUrl,
+          `/projects/${collection.id}/versions/${fragment.id}/details`,
+        ),
+        ctaText: 'MANUSCRIPT DETAILS',
+      },
+    })
+
+    if (isEiC) {
+      sendInvitedHEEmail({
+        email,
+        baseUrl,
+        eicName,
+        isCanceled,
+        titleText,
+        subjectBaseText,
+        handlingEditor: invitedHE,
+      })
+    } else {
+      sendEiCEmail({
+        eic,
+        email,
+        baseUrl,
+        comments: reason ? `Reason: "${reason}"` : '',
+        titleText,
+        isAccepted,
+        subjectBaseText,
+        targetUserName: `${invitedHE.firstName} ${invitedHE.lastName}`,
+      })
+    }
+  },
+}
+
+const sendInvitedHEEmail = ({
+  email,
+  eicName,
+  baseUrl,
+  titleText,
+  isCanceled,
+  handlingEditor,
+  subjectBaseText,
+}) => {
+  email.toUser = {
+    email: handlingEditor.email,
+    name: `${handlingEditor.firstName} ${handlingEditor.lastName}`,
+  }
+
+  email.content.subject = isCanceled
+    ? `${subjectBaseText} Assignment Revoked`
+    : `${subjectBaseText} Assignment`
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: handlingEditor.id,
+  })
+  email.content.signatureName = eicName
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType: isCanceled ? 'he-revoked' : 'he-assigned',
+      titleText,
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
+
+const sendEiCEmail = async ({
+  eic,
+  email,
+  baseUrl,
+  comments,
+  titleText,
+  isAccepted,
+  targetUserName,
+  subjectBaseText,
+}) => {
+  email.content.subject = `${subjectBaseText} Assignment Response`
+  const emailType = isAccepted ? 'he-accepted' : 'he-declined'
+
+  email.toUser = {
+    email: eic.email,
+    name: `${eic.firstName} ${eic.lastName}`,
+  }
+
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: eic.id,
+  })
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      comments,
+      emailType,
+      titleText,
+      targetUserName,
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
diff --git a/packages/component-invite/src/routes/collectionsInvitations/patch.js b/packages/component-invite/src/routes/collectionsInvitations/patch.js
index c8739d691db126ea8979f6544587ccd7ed344616..28452a347b73af471b0ff6eaaa8bbffc8e259df9 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/patch.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/patch.js
@@ -1,12 +1,10 @@
-const mailService = require('pubsweet-component-mail-service')
-
 const {
   Team,
-  User,
   services,
   Collection,
   Invitation,
 } = require('pubsweet-component-helper-service')
+const notifications = require('./emails/notifications')
 
 module.exports = models => async (req, res) => {
   const { collectionId, invitationId } = req.params
@@ -35,52 +33,33 @@ module.exports = models => async (req, res) => {
       })
 
     const collectionHelper = new Collection({ collection })
-    const baseUrl = services.getBaseUrl(req)
 
     const teamHelper = new Team({ TeamModel: models.Team, collectionId })
-    const userHelper = new User({ UserModel })
 
     await collectionHelper.updateHandlingEditor({ isAccepted })
     invitation.respondedOn = Date.now()
     invitation.hasAnswer = true
-    const eic = await userHelper.getEditorInChief()
-    const toEmail = eic.email
-
-    if (isAccepted) {
-      invitation.isAccepted = true
-      await collection.save()
+    invitation.isAccepted = isAccepted
 
-      mailService.sendSimpleEmail({
-        toEmail,
+    if (!isAccepted) {
+      await teamHelper.deleteHandlingEditor({
+        collection,
+        role: invitation.role,
         user,
-        emailType: 'handling-editor-agreed',
-        dashboardUrl: baseUrl,
-        meta: {
-          collectionId: collection.customId,
-        },
       })
 
-      return res.status(200).json(invitation)
+      if (reason) invitation.reason = reason
     }
 
-    await teamHelper.deleteHandlingEditor({
-      collection,
-      role: invitation.role,
-      user,
-    })
-
-    invitation.isAccepted = false
-    if (reason) invitation.reason = reason
     await collection.save()
 
-    mailService.sendSimpleEmail({
-      toEmail,
-      user,
-      emailType: 'handling-editor-declined',
-      meta: {
-        reason,
-        collectionId: collection.customId,
-      },
+    notifications.sendNotifications({
+      models,
+      reason,
+      collection,
+      isAccepted,
+      invitedHE: user,
+      baseUrl: services.getBaseUrl(req),
     })
 
     return res.status(200).json(invitation)
diff --git a/packages/component-invite/src/routes/collectionsInvitations/post.js b/packages/component-invite/src/routes/collectionsInvitations/post.js
index 3e20a3c89450bce8077a350249b150ac975dcab4..d1c3eae146fcb68eb139b8d5d4374b016ebdfa80 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/post.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/post.js
@@ -1,13 +1,15 @@
 const logger = require('@pubsweet/logger')
-const mailService = require('pubsweet-component-mail-service')
+
 const {
+  Team,
   services,
-  authsome: authsomeHelper,
   Collection,
-  Team,
   Invitation,
+  authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
 
+const notifications = require('./emails/notifications')
+
 module.exports = models => async (req, res) => {
   const { email, role } = req.body
 
@@ -50,7 +52,6 @@ module.exports = models => async (req, res) => {
     })
 
   const collectionHelper = new Collection({ collection })
-  const baseUrl = services.getBaseUrl(req)
 
   const teamHelper = new Team({
     TeamModel: models.Team,
@@ -83,11 +84,12 @@ module.exports = models => async (req, res) => {
     await collection.save()
     await collectionHelper.addHandlingEditor({ user, invitation })
 
-    mailService.sendSimpleEmail({
-      toEmail: user.email,
-      user,
-      emailType: 'assign-handling-editor',
-      dashboardUrl: baseUrl,
+    notifications.sendNotifications({
+      models,
+      collection,
+      isEiC: true,
+      invitedHE: user,
+      baseUrl: services.getBaseUrl(req),
     })
 
     return res.status(200).json(invitation)
diff --git a/packages/component-invite/src/routes/fragmentsInvitations/decline.js b/packages/component-invite/src/routes/fragmentsInvitations/decline.js
index eff9fb09c5feaba97fa5f516f9e53364aae88212..5ac57713ff800ef188b2a977fc89d4fc1f56d18b 100644
--- a/packages/component-invite/src/routes/fragmentsInvitations/decline.js
+++ b/packages/component-invite/src/routes/fragmentsInvitations/decline.js
@@ -1,11 +1,11 @@
 const {
-  Email,
   services,
-  Fragment,
   Invitation,
   authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
 
+const notifications = require('./emails/notifications')
+
 module.exports = models => async (req, res) => {
   const { collectionId, invitationId, fragmentId } = req.params
   const { invitationToken } = req.body
@@ -54,27 +54,17 @@ module.exports = models => async (req, res) => {
     invitation.isAccepted = false
     await fragment.save()
 
-    const fragmentHelper = new Fragment({ fragment })
-    const parsedFragment = await fragmentHelper.getFragmentData({
-      handlingEditor: collection.handlingEditor,
-    })
     const baseUrl = services.getBaseUrl(req)
-    const {
-      authorsList: authors,
-      submittingAuthor,
-    } = await fragmentHelper.getAuthorData({ UserModel })
-    const emailHelper = new Email({
-      UserModel,
-      collection,
-      parsedFragment,
+
+    notifications.sendNotifications({
       baseUrl,
-      authors,
-    })
-    emailHelper.setupReviewerDecisionEmail({
-      authorName: `${submittingAuthor.firstName} ${submittingAuthor.lastName}`,
-      agree: false,
-      user,
+      fragment,
+      collection,
+      reviewer: user,
+      UserModel: models.User,
+      emailType: 'reviewer-declined',
     })
+
     return res.status(200).json({})
   } catch (e) {
     const notFoundError = await services.handleNotFoundError(e, 'item')
diff --git a/packages/component-invite/src/routes/fragmentsInvitations/delete.js b/packages/component-invite/src/routes/fragmentsInvitations/delete.js
index af74f13d32a60e8cac0c18837b23c57b384d2570..242321653fd481990437566c9fa0741cb309e2b4 100644
--- a/packages/component-invite/src/routes/fragmentsInvitations/delete.js
+++ b/packages/component-invite/src/routes/fragmentsInvitations/delete.js
@@ -1,12 +1,12 @@
 const {
-  services,
   Team,
-  Email,
-  Fragment,
+  services,
   Collection,
   authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
 
+const notifications = require('./emails/notifications')
+
 module.exports = models => async (req, res) => {
   const { collectionId, invitationId, fragmentId } = req.params
   const teamHelper = new Team({
@@ -67,26 +67,16 @@ module.exports = models => async (req, res) => {
     user.teams = user.teams.filter(userTeamId => team.id !== userTeamId)
     await user.save()
 
-    const fragmentHelper = new Fragment({ fragment })
-    const parsedFragment = await fragmentHelper.getFragmentData({
-      handlingEditor: collection.handlingEditor,
-    })
     const baseUrl = services.getBaseUrl(req)
-    const {
-      authorsList: authors,
-      submittingAuthor,
-    } = await fragmentHelper.getAuthorData({ UserModel })
-    const emailHelper = new Email({
-      UserModel,
-      collection,
-      parsedFragment,
-      baseUrl,
-      authors,
-    })
 
-    emailHelper.setupReviewerUnassignEmail({
-      user,
-      authorName: `${submittingAuthor.firstName} ${submittingAuthor.lastName}`,
+    notifications.sendNotifications({
+      baseUrl,
+      fragment,
+      collection,
+      reviewer: user,
+      isCanceled: true,
+      UserModel: models.User,
+      emailType: 'reviewer-cancel-invitation',
     })
 
     return res.status(200).json({})
diff --git a/packages/component-invite/src/routes/fragmentsInvitations/emails/emailCopy.js b/packages/component-invite/src/routes/fragmentsInvitations/emails/emailCopy.js
new file mode 100644
index 0000000000000000000000000000000000000000..d21386e278a455acabebac452d45c93d29371aa4
--- /dev/null
+++ b/packages/component-invite/src/routes/fragmentsInvitations/emails/emailCopy.js
@@ -0,0 +1,64 @@
+const config = require('config')
+
+const getEmailCopy = ({
+  emailType,
+  titleText,
+  expectedDate,
+  targetUserName,
+}) => {
+  let upperContent, manuscriptText, lowerContent, paragraph
+  let hasLink = true
+  switch (emailType) {
+    case 'reviewer-invitation':
+      upperContent = `${titleText}, has been submitted for possible publication in Hindawi. As the Academic Editor handling the manuscript, I would be delighted if you would agree to review it and let me know whether you feel it is suitable for publication.`
+      manuscriptText =
+        "The manuscript's abstract, and author information is below to help you decide. Once you have agreed to review, you will be able to download the full article PDF."
+      lowerContent = `If a potential conflict of interest exists between yourself and either the authors or
+      the subject of the manuscript, please decline to handle the manuscript. If a conflict
+      becomes apparent during the review process, please let me know at the earliest possible
+      opportunity. For more information about our conflicts of interest policies, please
+      see:
+      <a href="https://www.hindawi.com/ethics/#coi">https://www.hindawi.com/ethics/#coi</a>.
+      If you are able to review the manuscript, I would be grateful if you could submit your
+      report by ${expectedDate}.`
+      break
+    case 'reviewer-resend-invitation':
+      upperContent = `On ${expectedDate} I sent you a request to review ${titleText}, submitted for possible publication in Hindawi.
+        I would be grateful if you would agree to review it and let me know whether you feel
+        it is suitable for publication. If you are unable to review this manuscript then
+        please decline to review. More details are available by clicking the link.`
+      manuscriptText =
+        "The manuscript's abstract, and author information is below to help you decide. Once you have agreed to review, you will be able to download the full article PDF."
+      lowerContent =
+        'I would like to thank you in advance for your help with the evaluation of this manuscript, since it would not be possible for us to run the journal without the help of our reviewers. I am looking forward to hearing from you.'
+      break
+    case 'reviewer-accepted':
+      paragraph = `We are pleased to inform you that Dr. ${targetUserName} has agreed to review ${titleText}. You should receive the report by Dr. ${targetUserName} before ${expectedDate}. If you have any queries, or would like to send a reminder if you no report has been submitted, then please visit the manuscript details page to see the full review.`
+      break
+    case 'reviewer-declined':
+      paragraph = `We regret to inform you that Dr. ${targetUserName} has declined to review ${titleText}. Please visit the manuscript details page to see if you need to invite any additional reviewers in order to reach a decision on the manuscript`
+      break
+    case 'reviewer-thank-you':
+      paragraph = `Thank you for agreeing to review ${titleText}. You can view the full PDF file of the manuscript and post your review report using the following URL:`
+      break
+    case 'reviewer-cancel-invitation':
+      paragraph = `You are no longer needed to review ${titleText}. If you have comments on this manuscript you believe the Editor should
+        see, please email them to ${config.get(
+          'mailer.from',
+        )} as soon as possible. Thank you for your
+        time and I hope you will consider reviewing for Hindawi again.`
+      hasLink = false
+      break
+    case 'reviewer-new-account':
+      paragraph = `You have been invited to review ${titleText}. In order to respond to the invitation, you need to confirm your account.`
+      break
+    default:
+      throw new Error(`The ${emailType} email type is not defined.`)
+  }
+
+  return { upperContent, manuscriptText, lowerContent, paragraph, hasLink }
+}
+
+module.exports = {
+  getEmailCopy,
+}
diff --git a/packages/component-invite/src/routes/fragmentsInvitations/emails/invitations.js b/packages/component-invite/src/routes/fragmentsInvitations/emails/invitations.js
new file mode 100644
index 0000000000000000000000000000000000000000..72fc21b73a18b09f8385d8d1697ebb42101891ec
--- /dev/null
+++ b/packages/component-invite/src/routes/fragmentsInvitations/emails/invitations.js
@@ -0,0 +1,125 @@
+const config = require('config')
+const { get } = require('lodash')
+
+const unsubscribeSlug = config.get('unsubscribe.url')
+const inviteReviewerPath = config.get('invite-reviewer.url')
+
+const {
+  Email,
+  services,
+  Fragment,
+} = require('pubsweet-component-helper-service')
+
+const { getEmailCopy } = require('./emailCopy')
+
+module.exports = {
+  async sendInvitations({
+    resend,
+    baseUrl,
+    fragment,
+    UserModel,
+    timestamp,
+    collection,
+    invitation,
+    invitedUser,
+  }) {
+    const fragmentHelper = new Fragment({ fragment })
+    const { title, abstract } = await fragmentHelper.getFragmentData({
+      handlingEditor: collection.handlingEditor,
+    })
+    const {
+      activeAuthors: authors,
+      submittingAuthor,
+    } = await fragmentHelper.getAuthorData({
+      UserModel,
+    })
+
+    const subjectBaseText = `${collection.customId}: Review`
+    const titleText = `The manuscript titled "${title}" by ${
+      submittingAuthor.firstName
+    } ${submittingAuthor.lastName}`
+
+    let queryParams = {
+      invitationId: invitation.id,
+      agree: true,
+    }
+
+    const detailsPath = `/projects/${collection.id}/versions/${
+      fragment.id
+    }/details`
+
+    const declineLink = services.createUrl(baseUrl, inviteReviewerPath, {
+      ...queryParams,
+      agree: false,
+      fragmentId: fragment.id,
+      collectionId: collection.id,
+      invitationToken: invitedUser.invitationToken,
+    })
+
+    let agreeLink = services.createUrl(baseUrl, detailsPath, queryParams)
+
+    if (!invitedUser.isConfirmed) {
+      queryParams = {
+        ...queryParams,
+        email: invitedUser.email,
+        token: invitedUser.passwordResetToken,
+        collectionId: collection.id,
+        fragmentId: fragment.id,
+        agree: true,
+      }
+      agreeLink = services.createUrl(baseUrl, inviteReviewerPath, queryParams)
+    }
+
+    const email = new Email({
+      type: 'user',
+      toUser: {
+        email: invitedUser.email,
+        name: `${invitedUser.firstName} ${invitedUser.lastName}`,
+      },
+      content: {
+        title,
+        abstract,
+        agreeLink,
+        declineLink,
+        subject: `${subjectBaseText} Requested`,
+        detailsLink: services.createUrl(baseUrl, detailsPath),
+        signatureName: get(collection, 'handlingEditor.name', 'Hindawi'),
+        unsubscribeLink: services.createUrl(baseUrl, unsubscribeSlug, {
+          id: invitedUser.id,
+        }),
+        authorsList: authors.map(
+          author => `${author.firstName} ${author.lastName}`,
+        ),
+      },
+    })
+
+    sendInvitedUserEmail({
+      email,
+      titleText,
+      resend,
+      timestamp,
+    })
+  },
+}
+
+const sendInvitedUserEmail = async ({
+  email,
+  titleText,
+  resend,
+  timestamp,
+}) => {
+  const emailType =
+    resend === true ? 'reviewer-resend-invitation' : 'reviewer-invitation'
+  const daysExpected = resend === true ? 0 : 14
+
+  const { html, text } = email.getBody({
+    isReviewerInvitation: true,
+    body: getEmailCopy({
+      emailType,
+      titleText,
+      expectedDate: services.getExpectedDate({ timestamp, daysExpected }),
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
diff --git a/packages/component-invite/src/routes/fragmentsInvitations/emails/notifications.js b/packages/component-invite/src/routes/fragmentsInvitations/emails/notifications.js
new file mode 100644
index 0000000000000000000000000000000000000000..12cad6a9d5283e486784a5a08c037b1b4c23d669
--- /dev/null
+++ b/packages/component-invite/src/routes/fragmentsInvitations/emails/notifications.js
@@ -0,0 +1,150 @@
+const config = require('config')
+const { get } = require('lodash')
+
+const unsubscribeSlug = config.get('unsubscribe.url')
+
+const {
+  User,
+  Email,
+  services,
+  Fragment,
+} = require('pubsweet-component-helper-service')
+
+const { getEmailCopy } = require('./emailCopy')
+
+module.exports = {
+  async sendNotifications({
+    baseUrl,
+    fragment,
+    reviewer,
+    UserModel,
+    emailType,
+    collection,
+    isNewUser = false,
+    isCanceled = false,
+  }) {
+    const fragmentHelper = new Fragment({ fragment })
+    const { title } = await fragmentHelper.getFragmentData({
+      handlingEditor: collection.handlingEditor,
+    })
+    const { submittingAuthor } = await fragmentHelper.getAuthorData({
+      UserModel,
+    })
+
+    const titleText = `the manuscript titled "${title}" by ${
+      submittingAuthor.firstName
+    } ${submittingAuthor.lastName}`
+
+    const handlingEditor = get(collection, 'handlingEditor')
+    const userHelper = new User({ UserModel })
+    const { firstName, lastName } = await userHelper.getEditorInChief()
+    const eicName = `${firstName} ${lastName}`
+    const subjectBaseText = isCanceled
+      ? `${collection.customId}: Reviewer `
+      : `${collection.customId}: Manuscript `
+
+    const email = new Email({
+      type: 'user',
+      content: {
+        signatureName: handlingEditor.name,
+        ctaLink: services.createUrl(
+          baseUrl,
+          `/projects/${collection.id}/versions/${fragment.id}/details`,
+        ),
+        ctaText: 'MANUSCRIPT DETAILS',
+      },
+    })
+
+    if (emailType !== 'reviewer-declined') {
+      sendReviewerEmail({
+        email,
+        baseUrl,
+        reviewer,
+        titleText,
+        isCanceled,
+        subjectBaseText,
+      })
+    }
+
+    if (['reviewer-accepted', 'reviewer-declined'].includes(emailType)) {
+      sendHandlingEditorEmail({
+        email,
+        eicName,
+        titleText,
+        emailType,
+        handlingEditor,
+        subjectBaseText,
+        targetUserName: `${reviewer.firstName} ${reviewer.lastName}`,
+      })
+    }
+  },
+}
+
+const sendHandlingEditorEmail = ({
+  email,
+  eicName,
+  baseUrl,
+  titleText,
+  emailType,
+  handlingEditor,
+  targetUserName,
+  subjectBaseText,
+}) => {
+  email.toUser = {
+    email: handlingEditor.email,
+    name: handlingEditor.name,
+  }
+
+  email.content.subject = `${subjectBaseText} Reviews`
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: handlingEditor.id,
+  })
+  email.content.signatureName = eicName
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType,
+      titleText,
+      expectedDate: services.getExpectedDate({
+        timestamp: Date.now(),
+        daysExpected: 14,
+      }),
+      targetUserName,
+    }),
+  })
+  email.sendEmail({ html, text })
+}
+
+const sendReviewerEmail = async ({
+  email,
+  baseUrl,
+  reviewer,
+  titleText,
+  isCanceled,
+  subjectBaseText,
+}) => {
+  email.content.subject = isCanceled
+    ? `${subjectBaseText} Unassigned`
+    : `${subjectBaseText} Review`
+  const emailType = isCanceled
+    ? 'reviewer-cancel-invitation'
+    : 'reviewer-thank-you'
+
+  email.toUser = {
+    email: reviewer.email,
+    name: `${reviewer.firstName} ${reviewer.lastName}`,
+  }
+
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: reviewer.id,
+  })
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType,
+      titleText,
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
diff --git a/packages/component-invite/src/routes/fragmentsInvitations/patch.js b/packages/component-invite/src/routes/fragmentsInvitations/patch.js
index 15c8359e94ff8a3c359252d70000050e5e43ed70..b6bda8da6ee35e84b28b992cee9c86b6102ce3d2 100644
--- a/packages/component-invite/src/routes/fragmentsInvitations/patch.js
+++ b/packages/component-invite/src/routes/fragmentsInvitations/patch.js
@@ -1,12 +1,12 @@
 const {
-  Email,
   services,
-  Fragment,
   Collection,
   Invitation,
   authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
 
+const notifications = require('./emails/notifications')
+
 module.exports = models => async (req, res) => {
   const { collectionId, invitationId, fragmentId } = req.params
   const { isAccepted, reason } = req.body
@@ -20,7 +20,9 @@ module.exports = models => async (req, res) => {
       return res.status(400).json({
         error: `Fragment ${fragmentId} does not match collection ${collectionId}`,
       })
+
     const fragment = await models.Fragment.find(fragmentId)
+
     fragment.invitations = fragment.invitations || []
     const invitation = await fragment.invitations.find(
       invitation => invitation.id === invitationId,
@@ -31,6 +33,7 @@ module.exports = models => async (req, res) => {
       role: 'reviewer',
       invitation,
     })
+
     const invitationValidation = invitationHelper.validateInvitation()
     if (invitationValidation.error)
       return res.status(invitationValidation.status).json({
@@ -45,54 +48,33 @@ module.exports = models => async (req, res) => {
       })
 
     const collectionHelper = new Collection({ collection })
-    const fragmentHelper = new Fragment({ fragment })
-    const parsedFragment = await fragmentHelper.getFragmentData({
-      handlingEditor: collection.handlingEditor,
-    })
     const baseUrl = services.getBaseUrl(req)
-    const {
-      authorsList: authors,
-      submittingAuthor,
-    } = await fragmentHelper.getAuthorData({ UserModel })
-    const emailHelper = new Email({
-      UserModel,
-      collection,
-      parsedFragment,
-      baseUrl,
-      authors,
-    })
 
     invitation.respondedOn = Date.now()
     invitation.hasAnswer = true
+    invitation.isAccepted = isAccepted
+
     if (isAccepted) {
-      invitation.isAccepted = true
-      if (collection.status === 'reviewersInvited')
-        await collectionHelper.updateStatus({ newStatus: 'underReview' })
+      if (collection.status === 'reviewersInvited') {
+        collectionHelper.updateStatus({ newStatus: 'underReview' })
+      }
+      fragment.save()
+    } else {
+      if (reason) invitation.reason = reason
       await fragment.save()
-
-      emailHelper.setupReviewerDecisionEmail({
-        agree: true,
-        timestamp: invitation.respondedOn,
-        user,
-        authorName: `${submittingAuthor.firstName} ${
-          submittingAuthor.lastName
-        }`,
+      collectionHelper.updateStatusByNumberOfReviewers({
+        invitations: fragment.invitations,
       })
-
-      return res.status(200).json(invitation)
     }
 
-    invitation.isAccepted = false
-    if (reason) invitation.reason = reason
-    await fragment.save()
-
-    collectionHelper.updateStatusByNumberOfReviewers({
-      invitations: fragment.invitations,
-    })
-
-    emailHelper.setupReviewerDecisionEmail({
-      agree: false,
-      user,
+    notifications.sendNotifications({
+      baseUrl,
+      fragment,
+      collection,
+      reviewer: user,
+      UserModel: models.User,
+      emailType:
+        isAccepted === true ? 'reviewer-accepted' : 'reviewer-declined',
     })
 
     return res.status(200).json(invitation)
diff --git a/packages/component-invite/src/routes/fragmentsInvitations/post.js b/packages/component-invite/src/routes/fragmentsInvitations/post.js
index e6bab24045d9a4d10252cde11f6fb3da2d1daae5..b0935ef55d759310cdcc596e40e70353967370f9 100644
--- a/packages/component-invite/src/routes/fragmentsInvitations/post.js
+++ b/packages/component-invite/src/routes/fragmentsInvitations/post.js
@@ -1,15 +1,15 @@
 const logger = require('@pubsweet/logger')
 const {
-  Email,
+  Team,
+  User,
   services,
-  authsome: authsomeHelper,
-  Fragment,
   Collection,
-  Team,
   Invitation,
-  User,
+  authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
 
+const emailInvitations = require('./emails/invitations')
+
 module.exports = models => async (req, res) => {
   const { email, role } = req.body
 
@@ -59,23 +59,9 @@ module.exports = models => async (req, res) => {
     })
 
   const collectionHelper = new Collection({ collection })
-  const fragmentHelper = new Fragment({ fragment })
-  const handlingEditor = collection.handlingEditor || {}
-  const parsedFragment = await fragmentHelper.getFragmentData({
-    handlingEditor,
-  })
+
   const baseUrl = services.getBaseUrl(req)
-  const {
-    authorsList: authors,
-    submittingAuthor,
-  } = await fragmentHelper.getAuthorData({ UserModel })
-  const emailHelper = new Email({
-    UserModel,
-    collection,
-    parsedFragment,
-    baseUrl,
-    authors,
-  })
+
   const teamHelper = new Team({
     TeamModel: models.Team,
     collectionId,
@@ -102,10 +88,11 @@ module.exports = models => async (req, res) => {
     let resend = false
 
     if (invitation) {
-      if (invitation.hasAnswer)
+      if (invitation.hasAnswer) {
         return res
           .status(400)
           .json({ error: 'User has already replied to a previous invitation.' })
+      }
 
       invitation.invitedOn = Date.now()
       await fragment.save()
@@ -116,34 +103,30 @@ module.exports = models => async (req, res) => {
       })
     }
 
-    if (collection.status === 'heAssigned')
-      await collectionHelper.updateStatus({ newStatus: 'reviewersInvited' })
+    if (collection.status === 'heAssigned') {
+      collectionHelper.updateStatus({ newStatus: 'reviewersInvited' })
+    }
 
-    emailHelper.setupReviewerInvitationEmail({
-      user,
-      invitationId: invitation.id,
-      timestamp: invitation.invitedOn,
+    emailInvitations.sendInvitations({
       resend,
-      authorName: `${submittingAuthor.firstName} ${submittingAuthor.lastName}`,
+      baseUrl,
+      fragment,
+      collection,
+      invitation,
+      invitedUser: user,
+      UserModel: models.User,
+      timestamp: invitation.invitedOn,
     })
 
     return res.status(200).json(invitation)
   } catch (e) {
     const userHelper = new User({ UserModel })
 
-    const newUser = await userHelper.setupNewUser({
-      url: baseUrl,
+    const newUser = await userHelper.createUser({
       role,
-      invitationType: 'invite',
       body: req.body,
     })
 
-    if (newUser.error !== undefined) {
-      return res.status(newUser.status).json({
-        error: newUser.message,
-      })
-    }
-
     if (collection.status === 'heAssigned')
       await collectionHelper.updateStatus({ newStatus: 'reviewersInvited' })
 
@@ -155,11 +138,14 @@ module.exports = models => async (req, res) => {
       parentObject: fragment,
     })
 
-    emailHelper.setupReviewerInvitationEmail({
-      user: newUser,
-      invitationId: invitation.id,
+    emailInvitations.sendInvitations({
+      baseUrl,
+      fragment,
+      collection,
+      invitation,
+      invitedUser: newUser,
+      UserModel: models.User,
       timestamp: invitation.invitedOn,
-      authorName: `${submittingAuthor.firstName} ${submittingAuthor.lastName}`,
     })
 
     return res.status(200).json(invitation)
diff --git a/packages/component-invite/src/tests/collectionsInvitations/delete.test.js b/packages/component-invite/src/tests/collectionsInvitations/delete.test.js
index 7fdef707ccadb721ce6099a69001da73ec29c552..c6e6089b02c4946f6a624911036cf2736a905d77 100644
--- a/packages/component-invite/src/tests/collectionsInvitations/delete.test.js
+++ b/packages/component-invite/src/tests/collectionsInvitations/delete.test.js
@@ -6,8 +6,8 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 
 const path = '../routes/collectionsInvitations/delete'
diff --git a/packages/component-invite/src/tests/collectionsInvitations/patch.test.js b/packages/component-invite/src/tests/collectionsInvitations/patch.test.js
index 11c7b1ded658ae9a41fabddd636d7a48f6d2560b..3bc33a435bdfe9cdb530614955e8146929218b2a 100644
--- a/packages/component-invite/src/tests/collectionsInvitations/patch.test.js
+++ b/packages/component-invite/src/tests/collectionsInvitations/patch.test.js
@@ -6,10 +6,8 @@ const cloneDeep = require('lodash/cloneDeep')
 const fixturesService = require('pubsweet-component-fixture-service')
 
 const { Model, fixtures } = fixturesService
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
-  sendReviewerInvitationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 
 const reqBody = {
diff --git a/packages/component-invite/src/tests/collectionsInvitations/post.test.js b/packages/component-invite/src/tests/collectionsInvitations/post.test.js
index 0608aced450fbf604c14fb2af509309262808ddc..18e7ce2b1944aa5f6c33fbe0ae9fb60e56ee791b 100644
--- a/packages/component-invite/src/tests/collectionsInvitations/post.test.js
+++ b/packages/component-invite/src/tests/collectionsInvitations/post.test.js
@@ -8,11 +8,10 @@ const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
 
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
-  sendReviewerInvitationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 const chance = new Chance()
 const reqBody = {
   email: chance.email(),
diff --git a/packages/component-invite/src/tests/fragmentsInvitations/decline.test.js b/packages/component-invite/src/tests/fragmentsInvitations/decline.test.js
index 5365092f9902a39574356886fdcb448f5404e767..0357e6c500ccc2a865352b93715dd8ea6fed7c83 100644
--- a/packages/component-invite/src/tests/fragmentsInvitations/decline.test.js
+++ b/packages/component-invite/src/tests/fragmentsInvitations/decline.test.js
@@ -7,8 +7,8 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const { Model, fixtures } = fixturesService
 const cloneDeep = require('lodash/cloneDeep')
 
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 
 const reqBody = {
diff --git a/packages/component-invite/src/tests/fragmentsInvitations/delete.test.js b/packages/component-invite/src/tests/fragmentsInvitations/delete.test.js
index e19c54ffa1e569dd2b13d579d04ac2ddce3ed099..d7ee683a82165b31adf4b13eb53779be1c1d8b9c 100644
--- a/packages/component-invite/src/tests/fragmentsInvitations/delete.test.js
+++ b/packages/component-invite/src/tests/fragmentsInvitations/delete.test.js
@@ -6,9 +6,8 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 
 const path = '../routes/fragmentsInvitations/delete'
diff --git a/packages/component-invite/src/tests/fragmentsInvitations/get.test.js b/packages/component-invite/src/tests/fragmentsInvitations/get.test.js
index b6d6ac08293b0bd359f8ddb08c11ae0a91dcdd03..d30877398cfcf319effc69b265f9f7a7025c295e 100644
--- a/packages/component-invite/src/tests/fragmentsInvitations/get.test.js
+++ b/packages/component-invite/src/tests/fragmentsInvitations/get.test.js
@@ -6,11 +6,10 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
-  sendReviewerInvitationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 const path = '../routes/fragmentsInvitations/get'
 const route = {
   path:
diff --git a/packages/component-invite/src/tests/fragmentsInvitations/patch.test.js b/packages/component-invite/src/tests/fragmentsInvitations/patch.test.js
index ce759e90a9e732a2b2c01de245284753a2665a56..8ce62849aeaf74fe31749028fa9cccf1834e6fff 100644
--- a/packages/component-invite/src/tests/fragmentsInvitations/patch.test.js
+++ b/packages/component-invite/src/tests/fragmentsInvitations/patch.test.js
@@ -6,10 +6,8 @@ const cloneDeep = require('lodash/cloneDeep')
 const fixturesService = require('pubsweet-component-fixture-service')
 
 const { Model, fixtures } = fixturesService
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
-  sendReviewerInvitationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 
 const reqBody = {
diff --git a/packages/component-invite/src/tests/fragmentsInvitations/post.test.js b/packages/component-invite/src/tests/fragmentsInvitations/post.test.js
index c8e76803c5c5b44bf3de1a3e25d5fb118c49ed7b..ce8719050cf2d722720964548e817c814bf2db55 100644
--- a/packages/component-invite/src/tests/fragmentsInvitations/post.test.js
+++ b/packages/component-invite/src/tests/fragmentsInvitations/post.test.js
@@ -8,11 +8,10 @@ const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
 
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
-  sendReviewerInvitationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 const chance = new Chance()
 const reqBody = {
   email: chance.email(),
diff --git a/packages/component-mail-service/.gitignore b/packages/component-mail-service/.gitignore
deleted file mode 100644
index 3614a810088d89d9ccaa28d82401545634874a18..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-_build/
-api/
-logs/
-node_modules/
-uploads/
-.env.*
-.env
-config/local*.*
\ No newline at end of file
diff --git a/packages/component-mail-service/README.md b/packages/component-mail-service/README.md
deleted file mode 100644
index 03c6e7f275ddd7d6d930b1ea18d3d2653133953a..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Mail Service
-
-## Configuration
-
-In order to use this component, simply add the desired templates in the `src/templates/` folder and use them by matching the `emailType` parameter with the template name.
-
-## Usage
-
-Once you have your template setup, simply add a a case in the `switch` statement from `src/Mail.js`:
-
-```js
-switch (emailType) {
-  case 'invitation-email':
-    subject = 'You have been invited!'
-    break
-  default:
-    subject = 'Welcome!'
-    break
-}
-```
diff --git a/packages/component-mail-service/index.js b/packages/component-mail-service/index.js
deleted file mode 100644
index 3d0b3c14535e01c151f895accdfd5e0ccfc1294b..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/index.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('./src/Mail')
diff --git a/packages/component-mail-service/package.json b/packages/component-mail-service/package.json
deleted file mode 100644
index 5a8328e59fbbeab1b75e18cabfa51879627adc0d..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/package.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "name": "pubsweet-component-mail-service",
-  "version": "0.0.1",
-  "description": "mail service component for pubsweet",
-  "license": "MIT",
-  "author": "Collaborative Knowledge Foundation",
-  "files": [
-    "src"
-  ],
-  "main": "index.js",
-  "repository": {
-    "type": "git",
-    "url": "https://gitlab.coko.foundation/xpub/xpub",
-    "path": "component-mail-service"
-  },
-  "dependencies": {
-    "@pubsweet/component-send-email": "0.2.1",
-    "body-parser": "^1.17.2",
-    "handlebars": "^4.0.11"
-  },
-  "peerDependencies": {
-    "@pubsweet/logger": "^0.0.1",
-    "pubsweet-server": "^1.0.1"
-  },
-  "publishConfig": {
-    "access": "public"
-  }
-}
diff --git a/packages/component-mail-service/src/Mail.js b/packages/component-mail-service/src/Mail.js
deleted file mode 100644
index 5bb850405ba7d10c006d211d259fcaea479065ae..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/Mail.js
+++ /dev/null
@@ -1,687 +0,0 @@
-const Email = require('@pubsweet/component-send-email')
-const config = require('config')
-const helpers = require('./helpers/helpers')
-
-const forgotPath = config.get('forgot-password.url')
-const unsubscribeSlug = config.get('unsubscribe.url')
-const confirmSignUp = config.get('confirm-signup.url')
-const resetPath = config.get('invite-reset-password.url')
-const resetPasswordPath = config.get('invite-reviewer.url')
-
-module.exports = {
-  sendSimpleEmail: async ({
-    toId,
-    meta = {},
-    user = {},
-    toEmail = '',
-    emailType = '',
-    dashboardUrl = '',
-  }) => {
-    let subject, textBody
-    let emailTemplate = 'simpleCTA'
-    const replacements = {}
-    replacements.unsubscribeLink = helpers.createUrl(
-      dashboardUrl,
-      unsubscribeSlug,
-      {
-        id: toId,
-      },
-    )
-    switch (emailType) {
-      case 'assign-handling-editor':
-        subject = 'Hindawi Handling Editor Invitation'
-        replacements.headline =
-          'You have been assigned as a Handling Editor to a manuscript.'
-        replacements.paragraph =
-          'Please click on the link below to access your dashboard.'
-        replacements.previewText = 'An Editor in Chief has assigned you'
-        replacements.buttonText = 'VIEW DASHBOARD'
-        replacements.url = dashboardUrl
-
-        textBody = `${replacements.headline} ${replacements.paragraph} ${
-          replacements.url
-        } ${replacements.buttonText}`
-        break
-      case 'add-author':
-        subject = 'Manuscript Assignment on Hindawi'
-        replacements.headline =
-          'You have been assigned as an Author to a manuscript.'
-        replacements.paragraph =
-          "The manuscript will become visible on your dashboard once it's submitted. Please click on the link below to access your dashboard."
-        replacements.previewText = 'You are now an author'
-        replacements.buttonText = 'VIEW DASHBOARD'
-        replacements.url = dashboardUrl
-        textBody = `${replacements.headline} ${replacements.paragraph} ${
-          replacements.url
-        } ${replacements.buttonText}`
-        break
-      case 'signup':
-        subject = 'Confirm your email address'
-        replacements.headline = ''
-        replacements.paragraph =
-          'Please confirm your account by clicking on the link below.'
-        replacements.previewText = 'Hindawi account confirmation'
-        replacements.buttonText = 'CONFIRM'
-        replacements.url = helpers.createUrl(dashboardUrl, confirmSignUp, {
-          userId: user.id,
-          confirmationToken: meta.confirmationToken,
-        })
-        textBody = `${replacements.headline} ${replacements.paragraph} ${
-          replacements.url
-        } ${replacements.buttonText}`
-        break
-      case 'invite-author':
-        subject = 'Author Invitation'
-        replacements.headline =
-          'You have been invited to join Hindawi as an Author.'
-        replacements.paragraph =
-          'Please confirm your account and set your account details by clicking on the link below.'
-        replacements.previewText = 'You have been invited'
-        replacements.buttonText = 'CONFIRM'
-        replacements.url = helpers.createUrl(dashboardUrl, resetPath, {
-          email: user.email,
-          token: user.passwordResetToken,
-          firstName: user.firstName,
-          lastName: user.lastName,
-          affiliation: user.affiliation,
-          title: user.title,
-        })
-        textBody = `${replacements.headline} ${replacements.paragraph} ${
-          replacements.url
-        } ${replacements.buttonText}`
-        break
-      case 'invite':
-        subject = 'Hindawi Invitation'
-        replacements.headline = 'You have been invited to join Hindawi.'
-        replacements.paragraph =
-          'You can now join Hindawi. Please confirm your account and set your account details by clicking on the link below.'
-        replacements.previewText = 'You have been invited'
-        replacements.buttonText = 'CONFIRM'
-        replacements.url = helpers.createUrl(dashboardUrl, resetPath, {
-          email: user.email,
-          token: user.passwordResetToken,
-          firstName: user.firstName,
-          lastName: user.lastName,
-          affiliation: user.affiliation,
-          title: user.title,
-        })
-        textBody = `${replacements.headline} ${replacements.paragraph} ${
-          replacements.url
-        } ${replacements.buttonText}`
-        break
-      case 'handling-editor-agreed':
-        subject = 'Handling Editor Agreed'
-        replacements.headline = `${user.firstName} ${
-          user.lastName
-        } agreed to be a Handling Editor on manuscript ${meta.collectionId}.`
-        replacements.paragraph =
-          'Please click on the link below to access your dashboard.'
-        replacements.previewText = 'a user has is now handling editor'
-        replacements.buttonText = 'VIEW DASHBOARD'
-        replacements.url = dashboardUrl
-        textBody = `${replacements.headline} ${replacements.paragraph} ${
-          replacements.url
-        } ${replacements.buttonText}`
-        break
-      case 'handling-editor-declined':
-        subject = 'Handling Editor Declined'
-        replacements.headline = `${user.firstName} ${
-          user.lastName
-        } has declined to be a Handling Editor on manuscript ${
-          meta.collectionId
-        }.`
-        replacements.paragraph = meta.reason ? `Reason: "${meta.reason}"` : ''
-        replacements.previewText = 'a user has declined your invitation'
-        textBody = `${replacements.headline} ${replacements.paragraph}`
-        emailTemplate = 'noCTA'
-        break
-      case 'revoke-handling-editor':
-        subject = 'Invitation Has Been Cancelled'
-        replacements.headline =
-          'Your Handling Editor invitation to a manuscript has been revoked.'
-        replacements.paragraph = ''
-        replacements.previewText = 'you are no longer invited'
-        textBody = `${replacements.headline}`
-        emailTemplate = 'noCTA'
-        break
-      case 'manuscript-submitted':
-        subject = `${meta.collection.customId}: Manuscript Submitted`
-        replacements.previewText = 'A new manuscript has been submitted'
-        replacements.headline = `A new manuscript has been submitted.`
-        replacements.paragraph = `You can view the full manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        } and take further actions by clicking on the following link:`
-        replacements.buttonText = 'MANUSCRIPT DETAILS'
-        replacements.url = helpers.createUrl(
-          dashboardUrl,
-          `/projects/${meta.collection.id}/versions/${
-            meta.fragment.id
-          }/details`,
-        )
-        textBody = `${replacements.headline} ${replacements.paragraph} ${
-          replacements.url
-        } ${replacements.buttonText}`
-        break
-      case 'forgot-password':
-        subject = 'Forgot Password'
-        replacements.headline = 'You have requested a password reset.'
-        replacements.paragraph =
-          'In order to reset your password please click on the following link:'
-        replacements.previewText = 'Click button to reset your password'
-        replacements.buttonText = 'RESET PASSWORD'
-        replacements.url = helpers.createUrl(dashboardUrl, forgotPath, {
-          email: user.email,
-          token: user.passwordResetToken,
-        })
-        textBody = `${replacements.headline} ${replacements.paragraph} ${
-          replacements.url
-        } ${replacements.buttonText}`
-        break
-      default:
-        subject = 'Welcome to Hindawi!'
-        break
-    }
-
-    const htmlBody = helpers.getEmailBody(emailTemplate, replacements)
-    const mailData = {
-      from: config.get('mailer.from'),
-      to: toEmail,
-      subject,
-      text: textBody,
-      html: htmlBody,
-    }
-
-    return Email.send(mailData)
-  },
-  sendReviewerInvitationEmail: async ({
-    user,
-    baseUrl,
-    toId,
-    toEmail,
-    subject,
-    emailType,
-    meta = {},
-  }) => {
-    let queryParams = {
-      invitationId: meta.invitation.id,
-      agree: true,
-    }
-    const manuscriptDetailsLink = `/projects/${meta.collection.id}/versions/${
-      meta.fragment.id
-    }/details`
-
-    const declineUrl = helpers.createUrl(baseUrl, resetPasswordPath, {
-      ...queryParams,
-      agree: false,
-      fragmentId: meta.fragment.id,
-      collectionId: meta.collection.id,
-      invitationToken: user.invitationToken,
-    })
-
-    let agreeUrl = helpers.createUrl(
-      baseUrl,
-      manuscriptDetailsLink,
-      queryParams,
-    )
-
-    if (!user.isConfirmed) {
-      queryParams = {
-        ...queryParams,
-        email: user.email,
-        token: user.passwordResetToken,
-        collectionId: meta.collection.id,
-        fragmentId: meta.fragment.id,
-        agree: true,
-      }
-      agreeUrl = helpers.createUrl(baseUrl, resetPasswordPath, queryParams)
-    }
-    const replacements = {
-      agreeUrl,
-      declineUrl,
-      manuscriptDetailsUrl: helpers.createUrl(baseUrl, manuscriptDetailsLink),
-      title: meta.fragment.title,
-      authors: meta.fragment.authors,
-      abstract: meta.fragment.abstract,
-      previewText: 'invitation from Hindawi',
-      intro: `Dear ${user.firstName} ${user.lastName}`,
-      manuscriptText: '',
-      unsubscribeLink: helpers.createUrl(baseUrl, unsubscribeSlug, {
-        id: toId,
-      }),
-    }
-    let textBody
-    switch (emailType) {
-      case 'invite-reviewer':
-        replacements.upperContent = `A manuscript titled "${
-          meta.fragment.title
-        }" by ${meta.collection.authorName}, has been submitted
-        for possible publication in Hindawi. As the Academic Editor handling the manuscript,
-        I would be delighted if you would agree to review it and let me know whether you
-        feel it is suitable for publication.`
-
-        replacements.manuscriptText =
-          "The manuscript's abstract, and author information is below to help you decide. Once you have agreed to review, you will be able to download the full article PDF."
-
-        replacements.lowerContent = `If a potential conflict of interest exists between yourself and either the authors or
-          the subject of the manuscript, please decline to handle the manuscript. If a conflict
-          becomes apparent during the review process, please let me know at the earliest possible
-          opportunity. For more information about our conflicts of interest policies, please
-          see:
-          <a href="https://www.hindawi.com/ethics/#coi">https://www.hindawi.com/ethics/#coi</a>.
-          If you are able to review the manuscript, I would be grateful if you could submit your
-          report by ${helpers.getExpectedDate(meta.invitation.timestamp, 14)}.`
-
-        replacements.signatureName = meta.collection.handlingEditor.name
-        replacements.signatureEmail = meta.collection.handlingEditor.email
-        textBody = `${replacements.intro} ${replacements.upperContent} ${
-          replacements.agreeUrl
-        } ${replacements.declineUrl} ${replacements.manuscriptText} ${
-          replacements.lowerContent
-        } ${replacements.signature}`
-        break
-      case 'resend-reviewer':
-        replacements.upperContent = `On ${helpers.getExpectedDate(
-          meta.invitation.timestamp,
-          0,
-        )} I sent you a request to review the manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.collection.authorName
-        }, submitted for possible publication in Hindawi.
-          I would be grateful if you would agree to review it and let me know whether you feel
-          it is suitable for publication. If you are unable to review this manuscript then
-          please decline to review. More details are available by clicking the link.`
-
-        replacements.lowerContent =
-          'I would like to thank you in advance for your help with the evaluation of this manuscript, since it would not be possible for us to run the journal without the help of our reviewers. I am looking forward to hearing from you.'
-
-        replacements.signatureName = meta.collection.handlingEditor.name
-        replacements.signatureEmail = meta.collection.handlingEditor.email
-        textBody = `${replacements.intro} ${replacements.upperContent} ${
-          replacements.agreeUrl
-        } ${replacements.declineUrl} ${replacements.lowerContent} ${
-          replacements.signatureName
-        } ${replacements.signatureEmail}`
-        break
-      default:
-        break
-    }
-
-    const htmlBody = helpers.getInvitationBody('invitation', replacements)
-    const mailData = {
-      from: config.get('mailer.from'),
-      to: toEmail,
-      subject,
-      text: textBody,
-      html: htmlBody,
-    }
-    return Email.send(mailData)
-  },
-  sendNotificationEmail: async ({
-    user,
-    toId,
-    toEmail,
-    emailType,
-    meta = { privateNote: '' },
-  }) => {
-    let subject, textBody
-
-    const emailTemplate = 'notification'
-    const detailsUrl = meta.baseUrl
-      ? helpers.createUrl(
-          meta.baseUrl,
-          `/projects/${meta.collection.id}/versions/${
-            meta.fragment.id
-          }/details`,
-        )
-      : ''
-    const replacements = {
-      detailsUrl,
-      hasLink: true,
-      afterAnchor: '',
-      beforeAnchor: '',
-      unsubscribeLink: helpers.createUrl(meta.baseUrl, unsubscribeSlug, {
-        id: toId,
-      }),
-    }
-    switch (emailType) {
-      case 'unassign-reviewer':
-        subject = `${meta.collection.customId}: Review Unassigned`
-        replacements.previewText = 'notification from Hindawi'
-        replacements.intro = `Dear ${user.firstName} ${user.lastName}`
-
-        replacements.paragraph = `You are no longer needed to review the article titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        }. If you have comments on this manuscript you believe the Editor should
-          see, please email them to ${config.get(
-            'mailer.from',
-          )} as soon as possible. Thank you for your
-          time and I hope you will consider reviewing for Hindawi again.`
-        replacements.beforeAnchor = 'Please visit the'
-        replacements.afterAnchor = 'to see the full review'
-        replacements.signatureName = meta.handlingEditorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.afterAnchor} ${
-          replacements.signatureName
-        }`
-        break
-      case 'review-submitted':
-        subject = `${meta.collection.customId}: Manuscript Review`
-        replacements.previewText = 'a new review has been submitted'
-        replacements.intro = `Dear ${meta.handlingEditorName}`
-
-        replacements.paragraph = `We are pleased to inform you that Dr. ${
-          meta.reviewerName
-        } has submitted a review for the manuscript titled "${
-          meta.fragment.title
-        }" by ${meta.fragment.authorName}.`
-        replacements.beforeAnchor = 'Please visit the'
-        replacements.afterAnchor = 'to see the full review'
-
-        replacements.signatureName = meta.eicName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.afterAnchor} ${
-          replacements.signatureName
-        }`
-        break
-      case 'reviewer-agreed':
-        subject = `${meta.collection.customId}: Manuscript Reviews`
-        replacements.previewText = 'a user has agreed to review'
-        replacements.intro = `Dear ${meta.handlingEditorName}`
-
-        replacements.paragraph = `We are pleased to inform you that Dr. ${
-          user.firstName
-        } ${user.lastName} has agreed to review the manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        }. You should receive the report by Dr. ${user.firstName} ${
-          user.lastName
-        } before ${helpers.getExpectedDate(meta.timestamp, 14)}.`
-        replacements.beforeAnchor =
-          'If you have any queries, or would like to send a reminder if you no report has been submitted, then please visit the'
-        replacements.afterAnchor = 'to see the full review'
-
-        replacements.signatureName = meta.eicName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.afterAnchor} ${
-          replacements.signatureName
-        }`
-        break
-      case 'reviewer-declined':
-        subject = `${meta.collection.customId}: Manuscript Reviews`
-        replacements.previewText = 'a user has declined to review'
-        replacements.intro = `Dear ${meta.handlingEditorName}`
-        replacements.paragraph = `We regret to inform you that Dr. ${
-          user.firstName
-        } ${user.lastName} has declined to review the manuscript titled "${
-          meta.fragment.title
-        }" by ${meta.fragment.authorName}.`
-        replacements.beforeAnchor = 'Please visit the'
-        replacements.afterAnchor =
-          'to see if you need to invite any additional reviewers in order to reach a decision on the manuscript'
-        replacements.signatureName = meta.eicName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.afterAnchor} ${
-          replacements.signatureName
-        }`
-        break
-      case 'reviewer-thank-you':
-        subject = `${meta.collection.customId}: Manuscript Review`
-        replacements.previewText = 'Hindawi says thank you'
-        replacements.intro = `Dear Dr. ${user.firstName} ${user.lastName}`
-
-        replacements.paragraph = `Thank you for agreeing to review the manuscript titled "${
-          meta.fragment.title
-        }" by ${meta.fragment.authorName}.`
-        replacements.beforeAnchor =
-          'You can view the full PDF file of the manuscript and post your review report using the following URL:'
-        replacements.signatureName = meta.handlingEditorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.afterAnchor} ${
-          replacements.signatureName
-        }`
-        break
-      case 'agreed-reviewers-after-recommendation':
-        subject = meta.emailSubject
-        replacements.hasLink = false
-        replacements.previewText =
-          'a manuscript has received a recommendation based on reviews'
-        replacements.intro = `Dear Dr. ${meta.reviewerName}`
-
-        replacements.paragraph = `I appreciate any time you may have spent reviewing manuscript "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        }. However, an editorial decision has been made and the review of this manuscript is now complete. I apologize for any inconvenience. <br/>
-        If you have comments on this manuscript you believe the Editor should see, please email them to Hindawi as soon as possible. <br/>
-        Thank you for your interest and I hope you will consider reviewing for Hindawi again.`
-        delete replacements.detailsUrl
-        replacements.signatureName = meta.editorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.signatureName
-        }`
-        break
-      case 'no-response-reviewers-after-recommendation':
-        subject = meta.emailSubject
-        replacements.hasLink = false
-        replacements.previewText = 'a manuscript has reached a decision'
-        replacements.intro = `Dear Dr. ${meta.reviewerName}`
-
-        replacements.paragraph = `An editorial decision has been made regarding the ${
-          meta.manuscriptType
-        } titled "${meta.fragment.title}" by ${
-          meta.fragment.authorName
-        }. So, you do not need to proceed with the review of this manuscript. <br/><br/>
-        If you have comments on this manuscript you believe the Editor should see, please email them to Hindawi as soon as possible.`
-        delete replacements.detailsUrl
-        replacements.signatureName = meta.editorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.signatureName
-        }`
-        break
-      case 'eic-recommendation':
-        subject = `${meta.collection.customId}: Manuscript Recommendation`
-        replacements.previewText =
-          'a handling editor has submitted a recommendation'
-        replacements.intro = `Dear Dr. ${meta.eicName}`
-        replacements.paragraph = meta.paragraph
-        replacements.beforeAnchor =
-          'For more information about what is required, please visit the '
-        replacements.signatureName = meta.handlingEditorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.afterAnchor} ${
-          replacements.signatureName
-        }`
-        break
-      case 'author-request-to-revision':
-        subject = `${meta.collection.customId}: Manuscript Recommendation`
-        replacements.previewText =
-          'a handling editor has submitted a recommendation'
-        replacements.intro = `Dear Dr. ${meta.fragment.authorName}`
-        replacements.paragraph = `In order for the manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        } to proceed to publication, there needs to be a revision. <br/><br/>
-        ${meta.authorNoteText}<br/><br/>`
-        replacements.beforeAnchor =
-          'For more information about what is required, please visit the '
-        replacements.signatureName = meta.handlingEditorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.signatureName}`
-        break
-      case 'author-manuscript-rejected':
-        subject = `${meta.collection.customId}: Manuscript Rejected`
-        replacements.previewText = 'a manuscript has been rejected'
-        replacements.intro = `Dear Dr. ${meta.fragment.authorName}`
-        replacements.paragraph = `I am sorry to inform you that manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.submittingAuthorName
-        } has been rejected for publication. <br/><br/>
-        ${meta.authorNoteText}<br/><br/>`
-        replacements.hasLink = false
-        delete replacements.detailsUrl
-        replacements.signatureName = meta.handlingEditorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.signatureName
-        }`
-        break
-      case 'author-manuscript-published':
-        subject = `${meta.collection.customId}: Manuscript Decision`
-        replacements.previewText = 'a manuscript has been published'
-        replacements.intro = `Dear Dr. ${meta.fragment.authorName}`
-        replacements.paragraph = `I am delighted  to inform you that manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.submittingAuthorName
-        } has passed through the review process and will be published in Hindawi.<br/><br/>
-        ${meta.authorNoteText}<br/><br/>
-        Thanks again for choosing to publish with us.`
-        replacements.hasLink = false
-        delete replacements.detailsUrl
-        replacements.signatureName = meta.handlingEditorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.signatureName
-        }`
-        break
-      case 'he-manuscript-rejected':
-        subject = meta.emailSubject
-        replacements.hasLink = false
-        replacements.previewText = 'a manuscript has been rejected'
-        replacements.intro = `Dear Dr. ${meta.handlingEditorName}`
-
-        replacements.paragraph = `Thank you for your recommendation to reject the manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        } based on the reviews you received.<br/><br/>
-        I can confirm this article has now been rejected.`
-        delete replacements.detailsUrl
-        replacements.signatureName = meta.eicName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.signatureName
-        }`
-        break
-      case 'he-manuscript-published':
-        subject = meta.emailSubject
-        replacements.hasLink = false
-        replacements.previewText = 'a manuscript has been published'
-        replacements.intro = `Dear Dr. ${meta.handlingEditorName}`
-
-        replacements.paragraph = `Thank you for your recommendation to publish the manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        } based on the reviews you received.<br/><br/>
-        I can confirm this article will now go through to publication.`
-        delete replacements.detailsUrl
-        replacements.signatureName = meta.eicName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.signatureName
-        }`
-        break
-      case 'he-manuscript-return-with-comments':
-        subject = meta.emailSubject
-        replacements.hasLink = false
-        replacements.previewText =
-          'a manuscript has been returned with comments'
-        replacements.intro = `Dear Dr. ${meta.handlingEditorName}`
-
-        replacements.paragraph = `Thank you for your recommendation for the manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        } based on the reviews you received.<br/><br/>
-        ${meta.eicComments}<br/><br/>`
-        delete replacements.detailsUrl
-        replacements.signatureName = meta.eicName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.signatureName
-        }`
-        break
-      case 'submitting-reviewers-after-decision':
-        subject = meta.emailSubject
-        replacements.hasLink = false
-        replacements.previewText = 'a decision has been made on a manuscript'
-        replacements.intro = `Dear Dr. ${meta.reviewerName}`
-
-        replacements.paragraph = `Thank you for your review on the manuscript titled "${
-          meta.fragment.title
-        }" by ${
-          meta.fragment.authorName
-        }. After taking into account the reviews and the recommendation of the Handling Editor, I can confirm this article ${
-          meta.emailText
-        }.<br/><br/>
-        If you have any queries about this decision, then please email them to Hindawi as soon as possible.`
-        delete replacements.detailsUrl
-        replacements.signatureName = meta.editorName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.signatureName
-        }`
-        break
-      case 'new-version-submitted':
-        subject = `${meta.collection.customId}: Manuscript Update`
-        replacements.previewText = 'A manuscript has been updated'
-        replacements.intro = `Dear Dr. ${meta.handlingEditorName}`
-
-        replacements.paragraph = `A new version of the manuscript titled "${
-          meta.fragment.title
-        }" by ${meta.fragment.authorName} has been submitted.`
-        replacements.beforeAnchor =
-          'Previous reviewers have been automatically invited to review the manuscript again. Please visit the'
-        replacements.afterAnchor =
-          'to see the latest version and any other actions you may need to take'
-
-        replacements.signatureName = meta.eicName
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.afterAnchor} ${
-          replacements.signatureName
-        }`
-        break
-      case 'submitting-reviewers-after-revision':
-        subject = `${meta.collection.customId}: Manuscript Update`
-        replacements.previewText = 'A manuscript has been updated'
-        replacements.intro = `Dear Dr. ${meta.reviewerName}`
-
-        replacements.paragraph = `A new version of the manuscript titled "${
-          meta.fragment.title
-        }" by ${meta.fragment.authorName} has been submitted.`
-        replacements.beforeAnchor = `As you have reviewed the previous version of this manuscript, I would be grateful if you can review this revised version and submit a review report by ${helpers.getExpectedDate(
-          meta.timestamp,
-          14,
-        )}. You can download the PDF of the revised version and submit your new review from the following URL:`
-
-        replacements.signatureName = meta.collection.handlingEditor.name
-        textBody = `${replacements.intro} ${replacements.paragraph} ${
-          replacements.beforeAnchor
-        } ${replacements.detailsUrl} ${replacements.signatureName}`
-        break
-      default:
-        subject = 'Hindawi Notification!'
-        break
-    }
-    const htmlBody = helpers.getNotificationBody(emailTemplate, replacements)
-    const mailData = {
-      from: config.get('mailer.from'),
-      to: toEmail,
-      subject,
-      text: textBody,
-      html: htmlBody,
-    }
-    return Email.send(mailData)
-  },
-}
diff --git a/packages/component-mail-service/src/helpers/helpers.js b/packages/component-mail-service/src/helpers/helpers.js
deleted file mode 100644
index 12b4dafaebc92d85c55059e28d5974969f2dff3b..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/helpers/helpers.js
+++ /dev/null
@@ -1,126 +0,0 @@
-const querystring = require('querystring')
-const fs = require('fs')
-const handlebars = require('handlebars')
-
-const createUrl = (baseUrl, slug, queryParams = null) =>
-  !queryParams
-    ? `${baseUrl}${slug}`
-    : `${baseUrl}${slug}?${querystring.encode(queryParams)}`
-
-const getEmailBody = (emailType, replacements) => {
-  handlePartial('header', replacements)
-  handlePartial('footer', replacements)
-  handlePartial('mainButton', replacements)
-  handlePartial('mainBody', replacements)
-
-  return getMainTemplate(emailType, replacements)
-}
-
-const getNotificationBody = (emailType, replacements) => {
-  handlePartial('notificationHeader', replacements)
-  handlePartial('footer', replacements)
-  handlePartial('signature', replacements)
-  if (replacements.detailsUrl !== undefined)
-    handlePartial('manuscriptDetailsLink', replacements)
-  handlePartial('notificationBody', replacements)
-
-  return getMainTemplate(emailType, replacements)
-}
-
-const getInvitationBody = (emailType, replacements) => {
-  handlePartial('invitationHeader', replacements)
-  handlePartial('footer', replacements)
-  handlePartial('invitationUpperContent', replacements)
-  handlePartial('invitationButtons', replacements)
-  handlePartial('manuscriptData', replacements)
-  handlePartial('signature', replacements)
-  handlePartial('invitationLowerContent', replacements)
-
-  return getMainTemplate(emailType, replacements)
-}
-
-const getBody = (emailType, replacements) => {
-  const simplePartials = ['header', 'footer', 'mainButton', 'mainBody']
-  const notificationPartials = [
-    'notificationHeader',
-    'footer',
-    'signature',
-    'manuscriptDetailsLink',
-    'notificationBody',
-  ]
-  const invitationPartials = [
-    'invitationHeader',
-    'footer',
-    'invitationUpperContent',
-    'invitationButtons',
-    'manuscriptData',
-    'signature',
-    'invitationLowerContent',
-  ]
-
-  switch (emailType) {
-    case 'simpleCTA':
-    case 'noCTA':
-      simplePartials.forEach(partial => handlePartial(partial, replacements))
-      break
-    case 'invitation':
-      invitationPartials.forEach(partial =>
-        handlePartial(partial, replacements),
-      )
-      break
-    case 'notification':
-      notificationPartials.forEach(partial =>
-        handlePartial(partial, replacements),
-      )
-      break
-    default:
-      break
-  }
-  return getMainTemplate(emailType, replacements)
-}
-
-const readFile = path =>
-  fs.readFileSync(path, { encoding: 'utf-8' }, (err, file) => {
-    if (err) {
-      throw err
-    } else {
-      return file
-    }
-  })
-
-const handlePartial = (partialName = 'signature', context = {}) => {
-  let partial = readFile(
-    `${__dirname}/../templates/partials/${partialName}.hbs`,
-  )
-  const template = handlebars.compile(partial)
-  partial = template(context)
-  handlebars.registerPartial(partialName, partial)
-}
-
-const getMainTemplate = (fileName, context) => {
-  const htmlFile = readFile(`${__dirname}/../templates/${fileName}.html`)
-  const htmlTemplate = handlebars.compile(htmlFile)
-  const htmlBody = htmlTemplate(context)
-  return htmlBody
-}
-
-const getExpectedDate = (timestamp, daysExpected) => {
-  const date = new Date(timestamp)
-  let expectedDate = date.getDate() + daysExpected
-  date.setDate(expectedDate)
-  expectedDate = date.toLocaleDateString('en-US', {
-    day: 'numeric',
-    month: 'long',
-    year: 'numeric',
-  })
-  return expectedDate
-}
-
-module.exports = {
-  getBody,
-  createUrl,
-  getEmailBody,
-  getExpectedDate,
-  getNotificationBody,
-  getInvitationBody,
-}
diff --git a/packages/component-mail-service/src/templates/invitation.html b/packages/component-mail-service/src/templates/invitation.html
deleted file mode 100644
index e48b106ecb4b622b0131103e70f7caa86fe1fbe4..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/templates/invitation.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{{> invitationHeader }}
-{{> invitationUpperContent }}
-{{> invitationButtons }}
-{{> invitationLowerContent }}
-{{> footer }}
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/noCTA.html b/packages/component-mail-service/src/templates/noCTA.html
deleted file mode 100644
index 63214091779bd60eac26f4f52433156083f5033f..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/templates/noCTA.html
+++ /dev/null
@@ -1,3 +0,0 @@
-{{> header }}
-{{> mainBody }}
-{{> footer }}
diff --git a/packages/component-mail-service/src/templates/notification.html b/packages/component-mail-service/src/templates/notification.html
deleted file mode 100644
index 56554d0f77a46d29e2ecddae448d48c2176c5ce3..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/templates/notification.html
+++ /dev/null
@@ -1,3 +0,0 @@
-{{> notificationHeader}}
-{{> notificationBody}}
-{{> footer}}
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/partials/mainBody.hbs b/packages/component-mail-service/src/templates/partials/mainBody.hbs
deleted file mode 100644
index 6fb579666eab6f2477f7d40e50f7d563fa47bb80..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/templates/partials/mainBody.hbs
+++ /dev/null
@@ -1,10 +0,0 @@
-<table class="module" role="module" data-type="text" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
-  <tr>
-    <td style="padding:30px 23px 0px 23px;background-color:#ffffff;" height="100%" valign="top" bgcolor="#ffffff">
-      <h1 style="text-align: center;">{{ headline }}</h1>
-      <div style="text-align: center;">{{ paragraph }}</div>
-      <div style="text-align: center;">&nbsp;</div>
-      <div style="text-align: center;">&nbsp;</div>
-    </td>
-  </tr>
-</table>
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/partials/manuscriptDetailsLink.hbs b/packages/component-mail-service/src/templates/partials/manuscriptDetailsLink.hbs
deleted file mode 100644
index 133841f72b5f82aa572a674fd0d7a079619e689b..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/templates/partials/manuscriptDetailsLink.hbs
+++ /dev/null
@@ -1 +0,0 @@
- {{ beforeAnchor }} <a href="{{ detailsUrl }}">manuscript details page</a> {{ afterAnchor}}.
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/partials/notificationHeader.hbs b/packages/component-mail-service/src/templates/partials/notificationHeader.hbs
deleted file mode 100644
index 3e8efa3eaeff420f3bbf0e68bc88d48713410851..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/templates/partials/notificationHeader.hbs
+++ /dev/null
@@ -1,161 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html data-editor-version="2" class="sg-campaigns" xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
-  <!--[if !mso]><!-->
-  <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
-  <!--<![endif]-->
-  <!--[if (gte mso 9)|(IE)]>
-    <xml>
-    <o:OfficeDocumentSettings>
-    <o:AllowPNG/>
-    <o:PixelsPerInch>96</o:PixelsPerInch>
-    </o:OfficeDocumentSettings>
-    </xml>
-    <![endif]-->
-  <!--[if (gte mso 9)|(IE)]>
-    <style type="text/css">
-      body {width: 600px;margin: 0 auto;}
-      table {border-collapse: collapse;}
-      table, td {mso-table-lspace: 0pt;mso-table-rspace: 0pt;}
-      img {-ms-interpolation-mode: bicubic;}
-    </style>
-    <![endif]-->
-
-  <style type="text/css">
-    body,
-    p,
-    div {
-      font-family: helvetica, arial, sans-serif;
-      font-size: 14px;
-    }
-
-    body {
-      color: #626262;
-    }
-
-    body a {
-      color: #0D78F2;
-      text-decoration: none;
-    }
-
-    p {
-      margin: 0;
-      padding: 0;
-    }
-
-    table.wrapper {
-      width: 100% !important;
-      table-layout: fixed;
-      -webkit-font-smoothing: antialiased;
-      -webkit-text-size-adjust: 100%;
-      -moz-text-size-adjust: 100%;
-      -ms-text-size-adjust: 100%;
-    }
-
-    img.max-width {
-      max-width: 100% !important;
-    }
-
-    .column.of-2 {
-      width: 50%;
-    }
-
-    .column.of-3 {
-      width: 33.333%;
-    }
-
-    .column.of-4 {
-      width: 25%;
-    }
-
-    @media screen and (max-width:480px) {
-      .preheader .rightColumnContent,
-      .footer .rightColumnContent {
-        text-align: left !important;
-      }
-      .preheader .rightColumnContent div,
-      .preheader .rightColumnContent span,
-      .footer .rightColumnContent div,
-      .footer .rightColumnContent span {
-        text-align: left !important;
-      }
-      .preheader .rightColumnContent,
-      .preheader .leftColumnContent {
-        font-size: 80% !important;
-        padding: 5px 0;
-      }
-      table.wrapper-mobile {
-        width: 100% !important;
-        table-layout: fixed;
-      }
-      img.max-width {
-        height: auto !important;
-        max-width: 480px !important;
-      }
-      a.bulletproof-button {
-        display: block !important;
-        width: auto !important;
-        font-size: 80%;
-        padding-left: 0 !important;
-        padding-right: 0 !important;
-      }
-      .columns {
-        width: 100% !important;
-      }
-      .column {
-        display: block !important;
-        width: 100% !important;
-        padding-left: 0 !important;
-        padding-right: 0 !important;
-        margin-left: 0 !important;
-        margin-right: 0 !important;
-      }
-    }
-  </style>
-  <!--user entered Head Start-->
-
-  <!--End Head user entered-->
-</head>
-
-<body>
-  <center class="wrapper" data-link-color="#0D78F2" data-body-style="font-size: 14px; font-family: helvetica,arial,sans-serif; color: #626262; background-color: #F4F4F4;">
-    <div class="webkit">
-      <table cellpadding="0" cellspacing="0" border="0" width="100%" class="wrapper" bgcolor="#F4F4F4">
-        <tr>
-          <td valign="top" bgcolor="#F4F4F4" width="100%">
-            <table width="100%" role="content-container" class="outer" align="center" cellpadding="0" cellspacing="0" border="0">
-              <tr>
-                <td width="100%">
-                  <table width="100%" cellpadding="0" cellspacing="0" border="0">
-                    <tr>
-                      <td>
-                        <!--[if mso]>
-                          <center>
-                          <table><tr><td width="600">
-                          <![endif]-->
-                        <table width="100%" cellpadding="0" cellspacing="0" border="0" style="width: 100%; max-width:600px;" align="center">
-                          <tr>
-                            <td role="modules-container" style="padding: 0px 0px 0px 0px; color: #626262; text-align: left;" bgcolor="#F4F4F4" width="100%"
-                              align="left">
-
-                              <table class="module preheader preheader-hide" role="module" data-type="preheader" border="0" cellpadding="0" cellspacing="0"
-                                width="100%" style="display: none !important; mso-hide: all; visibility: hidden; opacity: 0; color: transparent; height: 0; width: 0;">
-                                <tr>
-                                  <td role="module-content">
-                                    <p>{{ previewText }}</p>
-                                  </td>
-                                </tr>
-                              </table>
-
-                              <table class="wrapper" role="module" data-type="image" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
-                                <tr>
-                                  <td style="font-size:6px;line-height:10px;padding:20px 0px 20px 0px;" valign="top" align="center">
-                                    <img class="max-width" border="0" style="display:block;color:#000000;text-decoration:none;font-family:Helvetica, arial, sans-serif;font-size:16px;max-width:10% !important;width:10%;height:auto !important;"
-                                      src="https://marketing-image-production.s3.amazonaws.com/uploads/bb39b20cf15e52c1c0933676e25f2b2402737c6560b8098c204ad6932b84eb2058804376dbc4db138c7a21dcaed9325bde36185648afac5bc97e3d73d4e12718.png"
-                                      alt="" width="60">
-                                  </td>
-                                </tr>
-                              </table>
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/simpleCTA.html b/packages/component-mail-service/src/templates/simpleCTA.html
deleted file mode 100644
index 4d5a97b25445b85f27874470ab76864ccc45b3dd..0000000000000000000000000000000000000000
--- a/packages/component-mail-service/src/templates/simpleCTA.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{{> header }}
-{{> mainBody }}
-{{> mainButton }}
-{{> footer }}
diff --git a/packages/component-manuscript-manager/package.json b/packages/component-manuscript-manager/package.json
index 55ffd9d800fbc02181a01ef7821f74637be8296b..bff0f5f07707c9609f93c58a7061b45a90a39541 100644
--- a/packages/component-manuscript-manager/package.json
+++ b/packages/component-manuscript-manager/package.json
@@ -24,7 +24,7 @@
   },
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
-    "pubsweet-component-mail-service": "0.0.1",
+    "@pubsweet/component-send-email": "0.2.4",
     "pubsweet-server": "^1.0.1",
     "component-helper-service": "0.0.1"
   },
diff --git a/packages/component-manuscript-manager/src/routes/fragments/notifications/emailCopy.js b/packages/component-manuscript-manager/src/routes/fragments/notifications/emailCopy.js
new file mode 100644
index 0000000000000000000000000000000000000000..6b7c64bcf95b2803bea159ba981f35817f150ea0
--- /dev/null
+++ b/packages/component-manuscript-manager/src/routes/fragments/notifications/emailCopy.js
@@ -0,0 +1,26 @@
+const getEmailCopy = ({ emailType, titleText, expectedDate, customId }) => {
+  let paragraph
+  switch (emailType) {
+    case 'he-new-version-submitted':
+      paragraph = `A new version of ${titleText} has been submitted.
+        Previous reviewers have been automatically invited to review the manuscript again. Please visit the manuscript details page to see the latest version and any other actions you may need to take.`
+      break
+    case 'submitted-reviewers-after-revision':
+      paragraph = `A new version of ${titleText} has been submitted. As you have reviewed the previous version of this manuscript, I would be grateful if you can review this revised version and submit a review report by ${expectedDate}. You can download the PDF of the revised version and submit your new review from the following URL:`
+      break
+    case 'eic-manuscript-submitted':
+      paragraph = `A new manuscript has been submitted. You can view ${titleText} and take further actions by clicking on the following link:`
+      break
+    case 'eqs-manuscript-submitted':
+      paragraph = `Manuscript ID ${customId} has been submitted and a package has been sent. Please click on the link below to either approve or reject the manuscript:`
+      break
+    default:
+      throw new Error(`The ${emailType} email type is not defined.`)
+  }
+
+  return { paragraph, hasLink: true }
+}
+
+module.exports = {
+  getEmailCopy,
+}
diff --git a/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js
new file mode 100644
index 0000000000000000000000000000000000000000..2d362b84035b7ff979560c3c9fd1ecd885fd4d49
--- /dev/null
+++ b/packages/component-manuscript-manager/src/routes/fragments/notifications/notifications.js
@@ -0,0 +1,180 @@
+const config = require('config')
+const { get } = require('lodash')
+
+const {
+  User,
+  Email,
+  services,
+  Fragment,
+} = require('pubsweet-component-helper-service')
+
+const { getEmailCopy } = require('./emailCopy')
+
+const unsubscribeSlug = config.get('unsubscribe.url')
+const editorialAssistantEmail = config.get('mailer.editorialAssistant')
+
+module.exports = {
+  async sendNotifications({
+    baseUrl,
+    fragment,
+    UserModel,
+    collection,
+    isNewVersion = false,
+    isTechnicalCheck,
+    isMajorRecommendation,
+  }) {
+    const fragmentHelper = new Fragment({ fragment })
+    const parsedFragment = await fragmentHelper.getFragmentData({
+      handlingEditor: collection.handlingEditor,
+    })
+    const { submittingAuthor } = await fragmentHelper.getAuthorData({
+      UserModel,
+    })
+
+    const subjectBaseText = `${collection.customId}: Manuscript`
+    const titleText = `the manuscript titled "${parsedFragment.title}" by ${
+      submittingAuthor.firstName
+    } ${submittingAuthor.lastName}`
+
+    const email = new Email({
+      type: 'user',
+      content: {
+        signatureName: get(collection, 'handlingEditor.name', 'Hindawi'),
+        ctaLink: services.createUrl(
+          baseUrl,
+          `/projects/${collection.id}/versions/${fragment.id}/details`,
+        ),
+        ctaText: 'MANUSCRIPT DETAILS',
+      },
+    })
+
+    const userHelper = new User({ UserModel })
+    const eic = await userHelper.getEditorInChief()
+    const eicName = `${eic.firstName} ${eic.lastName}`
+
+    if (isNewVersion) {
+      sendHandlingEditorEmail({
+        email,
+        eicName,
+        baseUrl,
+        titleText,
+        subjectBaseText,
+        handlingEditor: get(collection, 'handlingEditor', {}),
+      })
+    }
+
+    if (isMajorRecommendation) {
+      sendReviewersEmail({
+        email,
+        baseUrl,
+        titleText,
+        fragmentHelper,
+        subjectBaseText,
+      })
+    }
+
+    if (isTechnicalCheck) {
+      sendEQSEmail({
+        eic,
+        email,
+        baseUrl,
+        collection,
+        subjectBaseText,
+      })
+    }
+  },
+}
+
+const sendHandlingEditorEmail = ({
+  email,
+  eicName,
+  baseUrl,
+  titleText,
+  handlingEditor,
+  subjectBaseText,
+}) => {
+  const emailType = 'he-new-version-submitted'
+
+  email.toUser = {
+    email: handlingEditor.email,
+    name: handlingEditor.name,
+  }
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: handlingEditor.id,
+  })
+  email.content.signatureName = eicName
+  email.content.subject = `${subjectBaseText} Update`
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType,
+      titleText,
+    }),
+  })
+  email.sendEmail({ html, text })
+}
+
+const sendReviewersEmail = async ({
+  email,
+  baseUrl,
+  titleText,
+  UserModel,
+  fragmentHelper,
+  subjectBaseText,
+}) => {
+  email.content.subject = `${subjectBaseText} Update`
+  const emailType = 'submitted-reviewers-after-revision'
+
+  const reviewers = await fragmentHelper.getReviewers({
+    UserModel,
+    type: 'submitted',
+  })
+
+  reviewers.forEach(reviewer => {
+    email.toUser = {
+      email: reviewer.email,
+      name: `${reviewer.firstName} ${reviewer.lastName}`,
+    }
+
+    email.content.unsubscribeLink = services.createUrl(
+      baseUrl,
+      unsubscribeSlug,
+      {
+        id: reviewer.id,
+      },
+    )
+
+    const { html, text } = email.getBody({
+      body: getEmailCopy({
+        emailType,
+        titleText,
+        expectedDate: services.getExpectedDate({ daysExpected: 14 }),
+      }),
+    })
+
+    email.sendEmail({ html, text })
+  })
+}
+
+const sendEQSEmail = ({ eic, email, baseUrl, collection, subjectBaseText }) => {
+  const emailType = 'eqs-manuscript-submitted'
+
+  email.toUser = {
+    email: editorialAssistantEmail,
+    name: 'Editorial Assistant',
+  }
+
+  email.content.unsubscribeLink = baseUrl
+  email.content.signatureName = `${eic.firstName} ${eic.lastName}`
+  email.content.subject = `${subjectBaseText} Submitted`
+  email.content.ctaLink = ''
+  email.content.ctaText = 'MAKE DECISION'
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType,
+      customId: collection.customId,
+    }),
+  })
+  email.sendEmail({ html, text })
+}
diff --git a/packages/component-manuscript-manager/src/routes/fragments/patch.js b/packages/component-manuscript-manager/src/routes/fragments/patch.js
index 7359ed96fea78b83498499ff7fe56b51dfe3bdef..6f4cd8a9eb18956142727537add44c9ed28035e0 100644
--- a/packages/component-manuscript-manager/src/routes/fragments/patch.js
+++ b/packages/component-manuscript-manager/src/routes/fragments/patch.js
@@ -1,12 +1,14 @@
+const { union, omit } = require('lodash')
+
 const {
   Team,
-  Email,
   services,
   Fragment,
   Collection,
   authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
-const { union, omit } = require('lodash')
+
+const notifications = require('./notifications/notifications')
 
 module.exports = models => async (req, res) => {
   const { collectionId, fragmentId } = req.params
@@ -49,7 +51,10 @@ module.exports = models => async (req, res) => {
     const newFragmentBody = {
       ...omit(fragment, ['revision', 'recommendations', 'id']),
       ...fragment.revision,
-      invitations: await fragmentHelper.getInvitationsForSubmittingReviewers(),
+      invitations: fragmentHelper.getInvitations({
+        isAccepted: true,
+        type: 'submitted',
+      }),
       version: fragment.version + 1,
       created: new Date(),
     }
@@ -110,27 +115,14 @@ module.exports = models => async (req, res) => {
     collection.fragments.push(newFragment.id)
     collection.save()
 
-    const parsedFragment = await fragmentHelper.getFragmentData({
-      handlingEditor: collection.handlingEditor,
-    })
-    const authors = await fragmentHelper.getAuthorData({
-      UserModel: models.User,
-    })
-    const email = new Email({
-      authors,
+    notifications.sendNotifications({
+      fragment,
       collection,
-      parsedFragment: { ...parsedFragment, id: fragment.id },
+      isNewVersion: true,
       UserModel: models.User,
       baseUrl: services.getBaseUrl(req),
+      isMajorRecommendation: heRecommendation.recommendation === 'major',
     })
-    email.setupNewVersionSubmittedEmail()
-
-    if (heRecommendation.recommendation === 'major') {
-      email.sendNewVersionSubmittedReviewersEmail({
-        invitations: newFragment.invitations,
-        newFragmentId: newFragment.id,
-      })
-    }
 
     return res.status(200).json(newFragment)
   } catch (e) {
diff --git a/packages/component-manuscript-manager/src/routes/fragments/post.js b/packages/component-manuscript-manager/src/routes/fragments/post.js
index 97ad2389943da68a469015f21cf483f465cdd1ff..7efc61b1055778bf8e04b30447eabf8dbabbed91 100644
--- a/packages/component-manuscript-manager/src/routes/fragments/post.js
+++ b/packages/component-manuscript-manager/src/routes/fragments/post.js
@@ -1,12 +1,12 @@
 const config = require('config')
 const { get } = require('lodash')
 const {
-  Email,
-  Fragment,
   services,
   authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
 
+const notifications = require('./notifications/notifications')
+
 const s3Config = get(config, 'pubsweet-component-aws-s3', {})
 const mtsConfig = get(config, 'mts-service', {})
 const MTSService = require('pubsweet-component-mts-package')
@@ -37,25 +37,8 @@ module.exports = models => async (req, res) => {
     fragment.submitted = Date.now()
     fragment = await fragment.save()
 
-    const fragmentHelper = new Fragment({ fragment })
-    const parsedFragment = await fragmentHelper.getFragmentData({
-      handlingEditor: collection.handlingEditor,
-    })
-    const authors = await fragmentHelper.getAuthorData({
-      UserModel: models.User,
-    })
-
-    const email = new Email({
-      authors,
-      collection,
-      parsedFragment,
-      UserModel: models.User,
-      baseUrl: services.getBaseUrl(req),
-    })
-    email.setupManuscriptSubmittedEmail()
-
-    collection.status = 'submitted'
-    collection.save()
+    collection.status = 'technicalChecks'
+    await collection.save()
 
     const { journal, xmlParser, ftp } = mtsConfig
     const MTS = new MTSService(journal, xmlParser, s3Config, ftp)
@@ -69,6 +52,14 @@ module.exports = models => async (req, res) => {
 
     await MTS.sendPackage(packageFragment)
 
+    notifications.sendNotifications({
+      fragment,
+      collection,
+      UserModel: models.User,
+      isTechnicalCheck: true,
+      baseUrl: services.getBaseUrl(req),
+    })
+
     return res.status(200).json(fragment)
   } catch (e) {
     const notFoundError = await services.handleNotFoundError(e, 'Item')
diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js
new file mode 100644
index 0000000000000000000000000000000000000000..56d672c3325fe9dee1ae5892a19a04ca8c6b37a6
--- /dev/null
+++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/emailCopy.js
@@ -0,0 +1,74 @@
+const getEmailCopy = ({
+  emailType,
+  titleText,
+  comments = '',
+  targetUserName = '',
+}) => {
+  let paragraph
+  let hasLink = true
+  switch (emailType) {
+    case 'author-request-to-revision':
+      paragraph = `In order for ${titleText} to proceed to publication, there needs to be a revision. <br/><br/>
+      ${comments}<br/><br/>
+        For more information about what is required, please visit the manuscript details page.`
+      break
+    case 'author-manuscript-rejected':
+      paragraph = `I am sorry to inform you that ${titleText} has been rejected for publication. <br/><br/>
+    ${comments}<br/><br/>`
+      hasLink = false
+      break
+    case 'author-manuscript-published':
+      paragraph = `I am delighted to inform you that ${titleText} has passed through the review process and will be published in Hindawi.<br/><br/>
+    ${comments}<br/><br/>
+    Thanks again for choosing to publish with us.`
+      hasLink = false
+      break
+    case 'he-manuscript-rejected':
+      hasLink = false
+      paragraph = `Thank you for your recommendation to reject ${titleText} based on the reviews you received.<br/><br/>
+      I can confirm this article has now been rejected.`
+      break
+    case 'he-manuscript-published':
+      hasLink = false
+      paragraph = `Thank you for your recommendation to publish ${titleText} based on the reviews you received.<br/><br/>
+      I can confirm this article will now go through to publication.`
+      break
+    case 'he-manuscript-return-with-comments':
+      hasLink = false
+      paragraph = `Thank you for your recommendation for ${titleText} based on the reviews you received.<br/><br/>
+      ${comments}<br/><br/>`
+      break
+    case 'accepted-reviewers-after-recommendation':
+      hasLink = false
+      paragraph = `I appreciate any time you may have spent reviewing ${titleText}. However, an editorial decision has been made and the review of this manuscript is now complete. I apologize for any inconvenience. <br/>
+      If you have comments on this manuscript you believe the Editor should see, please email them to Hindawi as soon as possible. <br/>
+      Thank you for your interest and I hope you will consider reviewing for Hindawi again.`
+      break
+    case 'pending-reviewers-after-recommendation':
+      hasLink = false
+      paragraph = `An editorial decision has been made regarding ${titleText}. So, you do not need to proceed with the review of this manuscript. <br/><br/>
+      If you have comments on this manuscript you believe the Editor should see, please email them to Hindawi as soon as possible.`
+      break
+    case 'submitted-reviewers-after-publish':
+      hasLink = false
+      paragraph = `Thank you for your review on ${titleText}. After taking into account the reviews and the recommendation of the Handling Editor, I can confirm this article will now be published.<br/><br/>
+      If you have any queries about this decision, then please email them to Hindawi as soon as possible.`
+      break
+    case 'submitted-reviewers-after-reject':
+      hasLink = false
+      paragraph = `Thank you for your review on ${titleText}. After taking into account the reviews and the recommendation of the Handling Editor, I can confirm this article has now been rejected.<br/><br/>
+      If you have any queries about this decision, then please email them to Hindawi as soon as possible.`
+      break
+    case 'he-review-submitted':
+      paragraph = `We are pleased to inform you that Dr. ${targetUserName} has submitted a review for ${titleText}.`
+      break
+    default:
+      throw new Error(`The ${emailType} email type is not defined.`)
+  }
+
+  return { paragraph, hasLink }
+}
+
+module.exports = {
+  getEmailCopy,
+}
diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js
new file mode 100644
index 0000000000000000000000000000000000000000..ac1e57c293f884bd7c443d789439d8bf4fe8bb9b
--- /dev/null
+++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js
@@ -0,0 +1,325 @@
+const config = require('config')
+const { chain, get } = require('lodash')
+
+const {
+  User,
+  Email,
+  services,
+  Fragment,
+} = require('pubsweet-component-helper-service')
+
+const { getEmailCopy } = require('./emailCopy')
+
+const unsubscribeSlug = config.get('unsubscribe.url')
+
+module.exports = {
+  async sendNotifications({
+    baseUrl,
+    fragment,
+    UserModel,
+    collection,
+    targetUserName,
+    isEditorInChief,
+    newRecommendation,
+  }) {
+    const fragmentHelper = new Fragment({ fragment })
+    const parsedFragment = await fragmentHelper.getFragmentData({
+      handlingEditor: collection.handlingEditor,
+    })
+    const fragmentAuthors = await fragmentHelper.getAuthorData({ UserModel })
+
+    const subjectBaseText = `${collection.customId}: Manuscript`
+    const titleText = `the manuscript titled "${parsedFragment.title}" by ${
+      fragmentAuthors.submittingAuthor.firstName
+    } ${fragmentAuthors.submittingAuthor.lastName}`
+
+    const email = new Email({
+      type: 'user',
+      content: {
+        signatureName: get(collection, 'handlingEditor.name', 'Hindawi'),
+        ctaLink: services.createUrl(
+          baseUrl,
+          `/projects/${collection.id}/versions/${fragment.id}/details`,
+        ),
+        ctaText: 'MANUSCRIPT DETAILS',
+      },
+    })
+
+    const userHelper = new User({ UserModel })
+    const { firstName, lastName } = await userHelper.getEditorInChief()
+    const eicName = `${firstName} ${lastName}`
+
+    let comments
+    if (isEditorInChief) {
+      const eicComments = chain(newRecommendation)
+        .get('comments')
+        .find(comm => !comm.public)
+        .get('content')
+        .value()
+
+      comments = eicComments
+    }
+
+    if (isEditorInChief || newRecommendation.recommendationType === 'review') {
+      sendHandlingEditorEmail({
+        email,
+        eicName,
+        baseUrl,
+        comments,
+        titleText,
+        targetUserName,
+        subjectBaseText,
+        handlingEditor: get(collection, 'handlingEditor', {}),
+        recommendation: newRecommendation.recommendation,
+        recommendationType: newRecommendation.recommendationType,
+      })
+    }
+
+    if (
+      newRecommendation.recommendationType !== 'review' &&
+      newRecommendation.recommendation !== 'return-to-handling-editor'
+    ) {
+      sendAuthorsEmail({
+        email,
+        baseUrl,
+        titleText,
+        parsedFragment,
+        fragmentAuthors,
+        isEditorInChief,
+        subjectBaseText,
+        newRecommendation,
+      })
+
+      sendReviewersEmail({
+        email,
+        baseUrl,
+        UserModel,
+        titleText,
+        fragmentHelper,
+        isEditorInChief,
+        subjectBaseText,
+        recommendation: newRecommendation.recommendation,
+        handlingEditorName: get(collection, 'handlingEditor.name', 'Faraday'),
+      })
+    }
+  },
+}
+
+const sendHandlingEditorEmail = ({
+  email,
+  eicName,
+  baseUrl,
+  comments,
+  titleText,
+  handlingEditor,
+  targetUserName,
+  recommendation,
+  subjectBaseText,
+  recommendationType,
+}) => {
+  let emailType
+  if (recommendationType === 'review') {
+    email.content.subject = `${subjectBaseText} Review`
+    emailType = 'he-review-submitted'
+  } else {
+    email.content.subject = `${subjectBaseText} Decision`
+    switch (recommendation) {
+      case 'return-to-handling-editor':
+        emailType = 'he-manuscript-return-with-comments'
+        break
+      case 'publish':
+        emailType = 'he-manuscript-published'
+        break
+      case 'reject':
+        emailType = 'he-manuscript-rejected'
+        break
+      default:
+        throw new Error(`undefined recommendation: ${recommendation} `)
+    }
+  }
+
+  email.toUser = {
+    email: handlingEditor.email,
+    name: handlingEditor.name,
+  }
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: handlingEditor.id,
+  })
+  email.content.signatureName = eicName
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType,
+      titleText,
+      comments,
+      targetUserName,
+    }),
+  })
+  email.sendEmail({ html, text })
+}
+
+const sendAuthorsEmail = async ({
+  email,
+  baseUrl,
+  titleText,
+  isEditorInChief,
+  subjectBaseText,
+  fragmentAuthors,
+  newRecommendation,
+  parsedFragment: { heRecommendation },
+}) => {
+  let emailType, authors, comments
+
+  if (isEditorInChief) {
+    const heComments = get(heRecommendation, 'comments', [])
+    if (heComments.length) {
+      const publicComment = heComments.find(comm => comm.public)
+      const content = get(publicComment, 'content')
+      if (!content) {
+        throw new Error('a public comment cannot be without content')
+      }
+      comments = `Reason & Details: "${content}"`
+    }
+
+    if (newRecommendation.recommendation === 'publish') {
+      emailType = 'author-manuscript-published'
+      email.content.subject = `${subjectBaseText} Published`
+    } else {
+      emailType = 'author-manuscript-rejected'
+      email.content.subject = `${subjectBaseText} Rejected`
+    }
+
+    authors = fragmentAuthors.activeAuthors.map(author => ({
+      ...author,
+      ...getEmailCopy({
+        emailType,
+        titleText,
+        comments,
+      }),
+    }))
+  } else {
+    emailType = 'author-request-to-revision'
+    email.content.subject = `${subjectBaseText} Recommendation`
+    const authorNote = newRecommendation.comments.find(comm => comm.public)
+    const content = get(authorNote, 'content')
+    const authorNoteText = content ? `Reason & Details: "${content}"` : ''
+    authors = [
+      {
+        ...fragmentAuthors.submittingAuthor,
+        ...getEmailCopy({
+          emailType,
+          titleText,
+          comments: authorNoteText,
+        }),
+      },
+    ]
+  }
+
+  authors.forEach(author => {
+    email.toUser = {
+      email: author.email,
+      name: `${author.firstName} ${author.lastName}`,
+    }
+    email.content.unsubscribeLink = services.createUrl(
+      baseUrl,
+      unsubscribeSlug,
+      {
+        id: author.id,
+      },
+    )
+    const { html, text } = email.getBody({
+      body: {
+        paragraph: author.paragraph,
+        hasLink: author.hasLink,
+      },
+    })
+    email.sendEmail({ html, text })
+  })
+}
+
+const sendReviewersEmail = async ({
+  email,
+  baseUrl,
+  eicName,
+  titleText,
+  UserModel,
+  fragmentHelper,
+  recommendation,
+  isEditorInChief,
+  subjectBaseText,
+  handlingEditorName,
+}) => {
+  let reviewers
+  if (isEditorInChief) {
+    email.content.subject = `${subjectBaseText} Decision`
+    const emailType =
+      recommendation === 'publish'
+        ? 'submitted-reviewers-after-publish'
+        : 'submitted-reviewers-after-reject'
+
+    reviewers = (await fragmentHelper.getReviewers({
+      UserModel,
+      type: 'submitted',
+    })).map(rev => ({
+      ...rev,
+      emailType,
+      ...getEmailCopy({
+        emailType,
+        titleText,
+      }),
+    }))
+    email.content.signatureName = eicName
+  } else {
+    email.content.signatureName = handlingEditorName
+    email.content.subject = `${subjectBaseText} ${getSubjectByRecommendation(
+      recommendation,
+    )}`
+
+    const acceptedReviewers = (await fragmentHelper.getReviewers({
+      UserModel,
+      type: 'accepted',
+    })).map(rev => ({
+      ...rev,
+      ...getEmailCopy({
+        emailType: 'accepted-reviewers-after-recommendation',
+        titleText,
+      }),
+    }))
+
+    const pendingReviewers = (await fragmentHelper.getReviewers({
+      UserModel,
+      type: 'pending',
+    })).map(rev => ({
+      ...rev,
+      ...getEmailCopy({
+        emailType: 'pending-reviewers-after-recommendation',
+        titleText,
+      }),
+    }))
+
+    reviewers = [...acceptedReviewers, ...pendingReviewers]
+  }
+
+  reviewers.forEach(reviewer => {
+    email.toUser = {
+      email: reviewer.email,
+      name: `${reviewer.firstName} ${reviewer.lastName}`,
+    }
+    email.content.unsubscribeLink = services.createUrl(
+      baseUrl,
+      unsubscribeSlug,
+      {
+        id: reviewer.id,
+      },
+    )
+    const { html, text } = email.getBody({
+      body: { paragraph: reviewer.paragraph },
+      hasLink: reviewer.hasLink,
+    })
+    email.sendEmail({ html, text })
+  })
+}
+
+const getSubjectByRecommendation = recommendation =>
+  ['minor', 'major'].includes(recommendation)
+    ? 'Revision Requested'
+    : 'Recommendation Submitted'
diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js
index 524276fb2d35b692a586cad981fcf7e38ab6b8a9..1e0e4f2425d884b64e5e0ad3c8616d83c371e8a3 100644
--- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js
+++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js
@@ -1,11 +1,11 @@
 const {
-  Email,
   services,
   authsome: authsomeHelper,
-  Fragment,
   Collection,
 } = require('pubsweet-component-helper-service')
 
+const notifications = require('./notifications/notifications')
+
 module.exports = models => async (req, res) => {
   const { collectionId, fragmentId, recommendationId } = req.params
   let collection, fragment
@@ -46,35 +46,26 @@ module.exports = models => async (req, res) => {
 
     Object.assign(recommendation, req.body)
     recommendation.updatedOn = Date.now()
-    if (req.body.submittedOn) {
-      const fragmentHelper = new Fragment({ fragment })
-      const parsedFragment = await fragmentHelper.getFragmentData({
-        handlingEditor: collection.handlingEditor,
-      })
-      const baseUrl = services.getBaseUrl(req)
-      const collectionHelper = new Collection({ collection })
-
-      const authors = await fragmentHelper.getAuthorData({
-        UserModel,
-      })
 
-      const email = new Email({
-        UserModel,
+    if (req.body.submittedOn) {
+      notifications.sendNotifications({
+        fragment,
         collection,
-        parsedFragment,
-        baseUrl,
-        authors,
-      })
-
-      email.setupHandlingEditorEmail({
-        reviewSubmitted: true,
-        reviewerName: `${user.firstName} ${user.lastName}`,
+        isEditorInChief: false,
+        UserModel: models.User,
+        baseUrl: services.getBaseUrl(req),
+        newRecommendation: recommendation,
+        targetUserName: `${user.firstName} ${user.lastName}`,
       })
 
-      if (['underReview'].includes(collection.status))
+      if (['underReview'].includes(collection.status)) {
+        const collectionHelper = new Collection({ collection })
         collectionHelper.updateStatus({ newStatus: 'reviewCompleted' })
+      }
     }
-    await fragment.save()
+
+    fragment.save()
+
     return res.status(200).json(recommendation)
   } catch (e) {
     const notFoundError = await services.handleNotFoundError(e, 'Item')
diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js
index f63cec1123e08e243a46641ca8fc21b5c75b8038..61b5cc07ca41eac18b8478acf375d291640defd3 100644
--- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js
+++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js
@@ -1,19 +1,22 @@
 const uuid = require('uuid')
-const { chain, pick } = require('lodash')
+const { pick } = require('lodash')
+
 const {
-  Email,
   services,
   authsome: authsomeHelper,
-  Fragment,
   Collection,
 } = require('pubsweet-component-helper-service')
 
+const notifications = require('./notifications/notifications')
+
 module.exports = models => async (req, res) => {
   const { recommendation, comments, recommendationType } = req.body
   if (!services.checkForUndefinedParams(recommendationType))
     return res.status(400).json({ error: 'Recommendation type is required.' })
 
   const reqUser = await models.User.find(req.user)
+  const isEditorInChief = reqUser.editorInChief || reqUser.admin
+
   const { collectionId, fragmentId } = req.params
 
   let collection, fragment
@@ -54,83 +57,30 @@ module.exports = models => async (req, res) => {
 
   newRecommendation.recommendation = recommendation || undefined
   newRecommendation.comments = comments || undefined
-  const UserModel = models.User
+
   const collectionHelper = new Collection({ collection })
-  const fragmentHelper = new Fragment({ fragment })
-  const parsedFragment = await fragmentHelper.getFragmentData({
-    handlingEditor: collection.handlingEditor,
+  collectionHelper.updateStatusOnRecommendation({
+    isEditorInChief,
+    recommendation,
   })
 
-  const baseUrl = services.getBaseUrl(req)
-  const authors = await fragmentHelper.getAuthorData({ UserModel })
-
-  const email = new Email({
-    UserModel,
-    collection,
-    parsedFragment,
-    baseUrl,
-    authors,
-  })
-  const FragmentModel = models.Fragment
+  if (!isEditorInChief && ['minor', 'major'].includes(recommendation)) {
+    fragment.revision = pick(fragment, ['authors', 'files', 'metadata'])
+  }
 
-  if (reqUser.editorInChief || reqUser.admin) {
-    if (recommendation === 'return-to-handling-editor') {
-      collectionHelper.updateStatus({ newStatus: 'reviewCompleted' })
-      const eicComments = chain(newRecommendation)
-        .get('comments')
-        .find(comm => !comm.public)
-        .get('content')
-        .value()
-      email.parsedFragment.eicComments = eicComments
-      email.setupHandlingEditorEmail({
-        returnWithComments: true,
-      })
-    } else {
-      collectionHelper.updateFinalStatusByRecommendation({
-        recommendation,
-      })
-      email.setupAuthorsEmail({
-        requestToRevision: false,
-        publish: recommendation === 'publish',
-        FragmentModel,
-      })
-      email.setupHandlingEditorEmail({
-        publish: recommendation === 'publish',
-      })
-      email.parsedFragment.recommendations = fragment.recommendations
-      email.setupReviewersEmail({
-        recommendation,
-        isSubmitted: true,
-        agree: true,
-        FragmentModel,
-      })
-    }
-  } else if (recommendationType === 'editorRecommendation') {
-    collectionHelper.updateStatusByRecommendation({
-      recommendation,
-      isHandlingEditor: true,
-    })
-    email.setupReviewersEmail({
-      recommendation,
-      agree: true,
-      FragmentModel,
+  if (recommendationType === 'editorRecommendation') {
+    notifications.sendNotifications({
+      fragment,
+      collection,
+      isEditorInChief,
+      newRecommendation,
+      UserModel: models.User,
+      baseUrl: services.getBaseUrl(req),
     })
-    email.setupReviewersEmail({ agree: false, FragmentModel: models.Fragment })
-    email.setupEiCEmail({
-      recommendation,
-      comments: newRecommendation.comments,
-    })
-
-    if (['minor', 'major'].includes(recommendation)) {
-      fragment.revision = pick(fragment, ['authors', 'files', 'metadata'])
-
-      email.parsedFragment.newComments = newRecommendation.comments
-      email.setupAuthorsEmail({
-        requestToRevision: true,
-      })
-    }
   }
+
   fragment.recommendations.push(newRecommendation)
-  await fragment.save()
+  fragment.save()
+
   return res.status(200).json(newRecommendation)
 }
diff --git a/packages/component-manuscript-manager/src/tests/fragments/patch.test.js b/packages/component-manuscript-manager/src/tests/fragments/patch.test.js
index 556203add45f77723b87e974158c501d53cd2e46..f4e2147a5604bf4f77e0782fdd1f33cb28ec9f58 100644
--- a/packages/component-manuscript-manager/src/tests/fragments/patch.test.js
+++ b/packages/component-manuscript-manager/src/tests/fragments/patch.test.js
@@ -6,8 +6,8 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 const reqBody = {}
 
diff --git a/packages/component-manuscript-manager/src/tests/fragments/post.test.js b/packages/component-manuscript-manager/src/tests/fragments/post.test.js
index 18fbd3612a51604c0dcec8d45f172eba0f197819..d9b12d000a7bedc669ae0e6ce73739571e88adc8 100644
--- a/packages/component-manuscript-manager/src/tests/fragments/post.test.js
+++ b/packages/component-manuscript-manager/src/tests/fragments/post.test.js
@@ -6,9 +6,8 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendNotificationEmail: jest.fn(),
-  sendSimpleEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 jest.mock('pubsweet-component-mts-package')
 const reqBody = {}
diff --git a/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/patch.test.js b/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/patch.test.js
index 05adf5f70e6b34b06cb859af787de6a53b6cb322..d67ae0ea44921e1a0781968f53949dcd67c7df98 100644
--- a/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/patch.test.js
+++ b/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/patch.test.js
@@ -7,12 +7,13 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 const chance = new Chance()
 const reqBody = {
-  recommendation: 'accept',
+  recommendation: 'publish',
   comments: [
     {
       content: chance.paragraph(),
diff --git a/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js b/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js
index 9013b742480d37eba74bd32157705009082b45e5..0f0f048e62835ecbd5d6c9df1f36ad3de740b183 100644
--- a/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js
+++ b/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js
@@ -8,9 +8,10 @@ const requests = require('../requests')
 
 const { Model, fixtures } = fixturesService
 const chance = new Chance()
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 const reqBody = {
   recommendation: 'accept',
   comments: [
diff --git a/packages/component-user-manager/package.json b/packages/component-user-manager/package.json
index 52b907483b0e7dac1aaf67cf1ee091b671cd809e..3008180e89d58577c35c345001d0cfbc1324b807 100644
--- a/packages/component-user-manager/package.json
+++ b/packages/component-user-manager/package.json
@@ -26,7 +26,7 @@
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
     "pubsweet-component-helper-service": "0.0.1",
-    "pubsweet-component-mail-service": "0.0.1",
+    "@pubsweet/component-send-email": "0.2.4",
     "pubsweet-server": "^1.0.1"
   },
   "devDependencies": {
diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js b/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js
new file mode 100644
index 0000000000000000000000000000000000000000..d6e87cc86947dee0dd26537455c9ed458a929500
--- /dev/null
+++ b/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js
@@ -0,0 +1,19 @@
+const getEmailCopy = ({ emailType, titleText }) => {
+  let paragraph
+  switch (emailType) {
+    case 'co-author-added-to-manuscript':
+      paragraph = `You have been added as co-author to ${titleText}. The manuscript will become visible on your dashboard once it's submitted. Please click on the link below to access your dashboard.`
+      break
+    case 'new-author-added-to-manuscript':
+      paragraph = `You have been added as an author to ${titleText}. In order to gain access to the manuscript, please confirm your account and set your account details by clicking on the link below.`
+      break
+    default:
+      throw new Error(`The ${emailType} email type is not defined.`)
+  }
+
+  return { paragraph, hasLink: true }
+}
+
+module.exports = {
+  getEmailCopy,
+}
diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js b/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js
new file mode 100644
index 0000000000000000000000000000000000000000..8ee396d5206c25e20e52c5145938a8242cbde576
--- /dev/null
+++ b/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js
@@ -0,0 +1,124 @@
+const config = require('config')
+
+const resetPath = config.get('invite-reset-password.url')
+const unsubscribeSlug = config.get('unsubscribe.url')
+
+const {
+  User,
+  Email,
+  services,
+  Fragment,
+} = require('pubsweet-component-helper-service')
+const { getEmailCopy } = require('./emailCopy')
+
+module.exports = {
+  async sendNotifications({
+    user,
+    baseUrl,
+    isSubmitting,
+    fragment,
+    UserModel,
+    collection,
+  }) {
+    const fragmentHelper = new Fragment({ fragment })
+    const { title } = await fragmentHelper.getFragmentData({
+      handlingEditor: collection.handlingEditor,
+    })
+    const { submittingAuthor } = await fragmentHelper.getAuthorData({
+      UserModel,
+    })
+
+    const titleText = `the manuscript titled "${title}" by ${
+      submittingAuthor.firstName
+    } ${submittingAuthor.lastName}`
+
+    const userHelper = new User({ UserModel })
+    const { firstName, lastName } = await userHelper.getEditorInChief()
+    const eicName = `${firstName} ${lastName}`
+    const subjectBaseText = `${collection.customId}: Manuscript`
+
+    const email = new Email({
+      type: 'user',
+      content: {
+        ctaLink: baseUrl,
+        ctaText: 'VIEW DASHBOARD',
+        signatureName: eicName,
+      },
+    })
+
+    if (!isSubmitting) {
+      sendCoAuthorEmail({ email, baseUrl, user, titleText, subjectBaseText })
+    }
+
+    sendNewAuthorEmail({
+      email,
+      user,
+      baseUrl,
+      titleText,
+      subjectBaseText,
+    })
+  },
+}
+
+const sendCoAuthorEmail = ({
+  email,
+  baseUrl,
+  user,
+  titleText,
+  subjectBaseText,
+}) => {
+  email.toUser = {
+    email: user.email,
+    name: `${user.firstName} ${user.lastName}`,
+  }
+
+  email.content.subject = `${subjectBaseText} Created`
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: user.id,
+  })
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType: 'co-author-added-to-manuscript',
+      titleText,
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
+
+const sendNewAuthorEmail = ({
+  email,
+  baseUrl,
+  user,
+  titleText,
+  subjectBaseText,
+}) => {
+  email.toUser = {
+    email: user.email,
+    name: `${user.firstName} ${user.lastName}`,
+  }
+
+  email.content.subject = `Confirm Your Account`
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: user.id,
+  })
+  email.content.ctaLink = services.createUrl(baseUrl, resetPath, {
+    email: user.email,
+    token: user.passwordResetToken,
+    firstName: user.firstName,
+    lastName: user.lastName,
+    affiliation: user.affiliation,
+    title: user.title,
+  })
+  email.content.ctaText = 'CONFIRM ACCOUNT'
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType: 'new-author-added-to-manuscript',
+      titleText,
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/get.js b/packages/component-user-manager/src/routes/fragmentsUsers/get.js
index 646163deaa6d0bc62f7129055e4cda04bf64cada..6798a5f55d82666a88d4e1adc8bdc2c732e9114a 100644
--- a/packages/component-user-manager/src/routes/fragmentsUsers/get.js
+++ b/packages/component-user-manager/src/routes/fragmentsUsers/get.js
@@ -11,6 +11,7 @@ module.exports = models => async (req, res) => {
       })
 
     const { authors = [] } = await models.Fragment.find(fragmentId)
+
     const userHelper = new User({ UserModel: models.User })
     const activeAuthors = await userHelper.getActiveAuthors(authors)
 
diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/post.js b/packages/component-user-manager/src/routes/fragmentsUsers/post.js
index 6edc714100c67b2aeff385fe0955f3fc64ac78ca..0d88c22046537c5d3c63bf08c2ac69d7d855e595 100644
--- a/packages/component-user-manager/src/routes/fragmentsUsers/post.js
+++ b/packages/component-user-manager/src/routes/fragmentsUsers/post.js
@@ -1,5 +1,5 @@
 const { pick } = require('lodash')
-const mailService = require('pubsweet-component-mail-service')
+const notifications = require('./emails/notifications')
 
 const {
   User,
@@ -105,18 +105,11 @@ module.exports = models => async (req, res) => {
 
     if (e.name === 'NotFoundError') {
       const userHelper = new User({ UserModel })
-      const newUser = await userHelper.setupNewUser({
-        url: baseUrl,
+      const newUser = await userHelper.createUser({
         role,
-        invitationType: 'invite-author',
         body: req.body,
       })
 
-      if (newUser.error !== undefined)
-        return res.status(newUser.status).json({
-          error: newUser.message,
-        })
-
       await teamHelper.setupTeam({
         user: newUser,
         role,
@@ -129,15 +122,18 @@ module.exports = models => async (req, res) => {
         isCorresponding,
       })
 
+      notifications.sendNotifications({
+        baseUrl,
+        fragment,
+        collection,
+        user: newUser,
+        UserModel: models.User,
+        isSubmitting,
+      })
+
       if (!collection.owners.includes(newUser.id)) {
         collection.owners.push(newUser.id)
         collection.save()
-        mailService.sendSimpleEmail({
-          toEmail: newUser.email,
-          user: newUser,
-          emailType: 'add-author',
-          dashboardUrl: baseUrl,
-        })
       }
 
       return res.status(200).json({
diff --git a/packages/component-user-manager/src/routes/users/emails/emailCopy.js b/packages/component-user-manager/src/routes/users/emails/emailCopy.js
new file mode 100644
index 0000000000000000000000000000000000000000..45d558e60775cb66e8c7a3ee837b4ea186d9b659
--- /dev/null
+++ b/packages/component-user-manager/src/routes/users/emails/emailCopy.js
@@ -0,0 +1,16 @@
+const getEmailCopy = ({ emailType }) => {
+  let paragraph
+  switch (emailType) {
+    case 'user-forgot-password':
+      paragraph = `You have requested a to reset your password. In order to reset your password please click on the following link:`
+      break
+    default:
+      throw new Error(`The ${emailType} email type is not defined.`)
+  }
+
+  return { paragraph, hasLink: true }
+}
+
+module.exports = {
+  getEmailCopy,
+}
diff --git a/packages/component-user-manager/src/routes/users/emails/notifications.js b/packages/component-user-manager/src/routes/users/emails/notifications.js
new file mode 100644
index 0000000000000000000000000000000000000000..20694fbc40149a28431fa66621085f8bf04cb76d
--- /dev/null
+++ b/packages/component-user-manager/src/routes/users/emails/notifications.js
@@ -0,0 +1,47 @@
+const config = require('config')
+
+const forgotPath = config.get('forgot-password.url')
+
+const unsubscribeSlug = config.get('unsubscribe.url')
+
+const { Email, services } = require('pubsweet-component-helper-service')
+
+const { getEmailCopy } = require('./emailCopy')
+
+module.exports = {
+  async sendNotifications({ user, baseUrl }) {
+    const email = new Email({
+      type: 'system',
+      content: {
+        ctaLink: services.createUrl(baseUrl, forgotPath, {
+          email: user.email,
+          token: user.passwordResetToken,
+        }),
+        ctaText: 'RESET PASSWORD',
+      },
+    })
+
+    sendForgotPasswordEmail({ email, baseUrl, user })
+  },
+}
+
+const sendForgotPasswordEmail = ({ email, baseUrl, user }) => {
+  email.toUser = {
+    email: user.email,
+    name: user.name,
+  }
+
+  email.content.subject = 'Forgot Password'
+  email.content.unsubscribeLink = services.createUrl(baseUrl, unsubscribeSlug, {
+    id: user.id,
+  })
+  email.content.signatureName = 'Hindawi Submission System'
+
+  const { html, text } = email.getBody({
+    body: getEmailCopy({
+      emailType: 'user-forgot-password',
+    }),
+  })
+
+  email.sendEmail({ html, text })
+}
diff --git a/packages/component-user-manager/src/routes/users/forgotPassword.js b/packages/component-user-manager/src/routes/users/forgotPassword.js
index aa54730cb0454f0ee4a3103020bf3f80b277ec8c..6ae4f87953355169f4de9440580e9976525622e7 100644
--- a/packages/component-user-manager/src/routes/users/forgotPassword.js
+++ b/packages/component-user-manager/src/routes/users/forgotPassword.js
@@ -3,7 +3,7 @@ const {
   services,
   authsome: authsomeHelper,
 } = require('pubsweet-component-helper-service')
-const mailService = require('pubsweet-component-mail-service')
+const notifications = require('./emails/notifications')
 
 module.exports = models => async (req, res) => {
   const { email } = req.body
@@ -36,12 +36,16 @@ module.exports = models => async (req, res) => {
     user.passwordResetTimestamp = Date.now()
     await user.save()
 
-    mailService.sendSimpleEmail({
-      toEmail: user.email,
+    notifications.sendNotifications({
       user,
-      emailType: 'forgot-password',
-      dashboardUrl: services.getBaseUrl(req),
+      baseUrl: services.getBaseUrl(req),
     })
+    // mailService.sendSimpleEmail({
+    //   toEmail: user.email,
+    //   user,
+    //   emailType: 'forgot-password',
+    //   dashboardUrl: services.getBaseUrl(req),
+    // })
   } catch (e) {
     logger.error(
       `A forgot password request has been made on an non-existent email: ${email}`,
diff --git a/packages/component-user-manager/src/tests/fragmentsUsers/delete.test.js b/packages/component-user-manager/src/tests/fragmentsUsers/delete.test.js
index 5bea3f85277e6cadf09fd9ff8b31ae5bbb15df7f..ac52444a6ce45f414d00609cad34086d7fc8e415 100644
--- a/packages/component-user-manager/src/tests/fragmentsUsers/delete.test.js
+++ b/packages/component-user-manager/src/tests/fragmentsUsers/delete.test.js
@@ -10,10 +10,10 @@ const { Model, fixtures } = fixturesService
 const { author, submittingAuthor } = fixtures.users
 const { collection } = fixtures.collections
 const deletePath = '../../routes/fragmentsUsers/delete'
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 describe('Delete fragments users route handler', () => {
   let testFixtures = {}
   let models
diff --git a/packages/component-user-manager/src/tests/fragmentsUsers/get.test.js b/packages/component-user-manager/src/tests/fragmentsUsers/get.test.js
index 290d26af0b1efa9ad4a9b59cf519b40c688e9787..bc9baf231f1992ff654bf1b3ba2afefa43f261f9 100644
--- a/packages/component-user-manager/src/tests/fragmentsUsers/get.test.js
+++ b/packages/component-user-manager/src/tests/fragmentsUsers/get.test.js
@@ -10,10 +10,10 @@ const { Model, fixtures } = fixturesService
 const { collection } = fixtures.collections
 const { submittingAuthor } = fixtures.users
 
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 const getPath = '../../routes/fragmentsUsers/get'
 describe('Get fragments users route handler', () => {
   let testFixtures = {}
diff --git a/packages/component-user-manager/src/tests/fragmentsUsers/post.test.js b/packages/component-user-manager/src/tests/fragmentsUsers/post.test.js
index 9df520c3fe8ccba600e7414cb86b6e7fb21c0597..91d1bf43173649124a07532d507b32ede32e9f17 100644
--- a/packages/component-user-manager/src/tests/fragmentsUsers/post.test.js
+++ b/packages/component-user-manager/src/tests/fragmentsUsers/post.test.js
@@ -8,10 +8,10 @@ const fixturesService = require('pubsweet-component-fixture-service')
 
 const { Model, fixtures } = fixturesService
 
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 const chance = new Chance()
 
 const { author, submittingAuthor, inactiveUser } = fixtures.users
diff --git a/packages/component-user-manager/src/tests/users/changePassword.test.js b/packages/component-user-manager/src/tests/users/changePassword.test.js
index 693dc6563d6c4dd7eab1a4b77cdc402531087e57..a97b1e0fe30e1c5605f7a30167d0034c9fd8e9d9 100644
--- a/packages/component-user-manager/src/tests/users/changePassword.test.js
+++ b/packages/component-user-manager/src/tests/users/changePassword.test.js
@@ -8,9 +8,8 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const { Model, fixtures } = fixturesService
 
 const { user } = fixtures.users
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 
 const reqBody = {
diff --git a/packages/component-user-manager/src/tests/users/confirm.test.js b/packages/component-user-manager/src/tests/users/confirm.test.js
index 1ce3a63e99551978007ddf43002c78cf21829935..0cf81bc161be3dd78733541aded262a6d5d34853 100644
--- a/packages/component-user-manager/src/tests/users/confirm.test.js
+++ b/packages/component-user-manager/src/tests/users/confirm.test.js
@@ -8,9 +8,8 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const { Model, fixtures } = fixturesService
 
 const { user, author, inactiveUser } = fixtures.users
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 
 const reqBody = {
diff --git a/packages/component-user-manager/src/tests/users/forgotPassword.test.js b/packages/component-user-manager/src/tests/users/forgotPassword.test.js
index d933b9f290b1260a95d62488fd754a5bacc5592d..39e7ddb71d6ad0e8232ba5c05e58338587bfc135 100644
--- a/packages/component-user-manager/src/tests/users/forgotPassword.test.js
+++ b/packages/component-user-manager/src/tests/users/forgotPassword.test.js
@@ -8,9 +8,8 @@ const fixturesService = require('pubsweet-component-fixture-service')
 const { Model, fixtures } = fixturesService
 
 const { user, author, inactiveUser } = fixtures.users
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
 
 const reqBody = {
diff --git a/packages/component-user-manager/src/tests/users/resetPassword.test.js b/packages/component-user-manager/src/tests/users/resetPassword.test.js
index 130526eb7f9bca6bf3cb00fd24a7e96cd3d8e2b7..fbdfe985f0c65f818dda72552f8ea9f9bdd5daff 100644
--- a/packages/component-user-manager/src/tests/users/resetPassword.test.js
+++ b/packages/component-user-manager/src/tests/users/resetPassword.test.js
@@ -10,10 +10,10 @@ const { Model, fixtures } = fixturesService
 const chance = new Chance()
 
 const { user } = fixtures.users
-jest.mock('pubsweet-component-mail-service', () => ({
-  sendSimpleEmail: jest.fn(),
-  sendNotificationEmail: jest.fn(),
+jest.mock('@pubsweet/component-send-email', () => ({
+  send: jest.fn(),
 }))
+
 const reqBody = {
   email: user.email,
   firstName: user.firstName,
diff --git a/packages/xpub-faraday/config/components.json b/packages/xpub-faraday/config/components.json
index 64925934b4897ed17dfe6abd56a33260464a41d2..cfea7f64f6fcb4263f9aaeb690b8b28de9f3eb3b 100644
--- a/packages/xpub-faraday/config/components.json
+++ b/packages/xpub-faraday/config/components.json
@@ -5,6 +5,7 @@
   "pubsweet-component-modal",
   "pubsweet-components-faraday",
   "@pubsweet/component-aws-s3",
+  "@pubsweet/component-send-email",
   "pubsweet-component-invite",
   "pubsweet-component-user-manager",
   "pubsweet-component-email",
diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js
index d7958d81f5985b02d32c1b026e20856fcb543f1d..945cc96a6ae9655316723d09687998939bbacee9 100644
--- a/packages/xpub-faraday/config/default.js
+++ b/packages/xpub-faraday/config/default.js
@@ -109,6 +109,7 @@ module.exports = {
   mailer: {
     from: 'faraday@hindawi.com',
     path: `${__dirname}/mailer`,
+    editorialAssistant: 'hindawi@thinslices.com',
   },
   publicKeys: [
     'pubsweet-client',
diff --git a/packages/xpub-faraday/package.json b/packages/xpub-faraday/package.json
index e4e829deab3a05be7c02ee7c4bd237ed68d18ca0..fe3ea72944c53c4188f83370c8c492ca2a151dac 100644
--- a/packages/xpub-faraday/package.json
+++ b/packages/xpub-faraday/package.json
@@ -11,6 +11,7 @@
     "@pubsweet/ui": "4.1.3",
     "@pubsweet/ui-toolkit": "latest",
     "@pubsweet/component-aws-s3": "^1.1.2",
+    "@pubsweet/component-send-email": "0.2.4",
     "aws-sdk": "^2.197.0",
     "babel-core": "^6.26.0",
     "config": "^1.26.2",
diff --git a/yarn.lock b/yarn.lock
index 965f00ccb37aa4b27d827dd63a31af4e9d7b5447..e570142079f288904ed7c9cdb33267013dab8903 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -104,9 +104,9 @@
     node-mocks-http "^1.6.6"
     nodemailer "^4.4.2"
 
-"@pubsweet/component-send-email@0.2.1":
-  version "0.2.1"
-  resolved "https://registry.yarnpkg.com/@pubsweet/component-send-email/-/component-send-email-0.2.1.tgz#9e70412cf20ce618ff42d14e4b09005e96d31ba2"
+"@pubsweet/component-send-email@0.2.4":
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/@pubsweet/component-send-email/-/component-send-email-0.2.4.tgz#e0d73769b804656d7ed5cebf1d2b50afaa378a02"
   dependencies:
     aws-sdk "^2.185.0"
     nodemailer "^4.4.2"
@@ -4354,7 +4354,7 @@ growly@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
 
-handlebars@^4.0.11, handlebars@^4.0.2, handlebars@^4.0.3:
+handlebars@^4.0.2, handlebars@^4.0.3:
   version "4.0.11"
   resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
   dependencies: