diff --git a/packages/component-helper-service/.gitignore b/packages/component-helper-service/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3614a810088d89d9ccaa28d82401545634874a18
--- /dev/null
+++ b/packages/component-helper-service/.gitignore
@@ -0,0 +1,8 @@
+_build/
+api/
+logs/
+node_modules/
+uploads/
+.env.*
+.env
+config/local*.*
\ No newline at end of file
diff --git a/packages/component-helper-service/README.md b/packages/component-helper-service/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a0ac7afbf2c8255c8a94f4f2ff525dd245cc3c93
--- /dev/null
+++ b/packages/component-helper-service/README.md
@@ -0,0 +1,2 @@
+# Helper Service
+
diff --git a/packages/component-helper-service/index.js b/packages/component-helper-service/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..e3c50cf86934045799e812f2851110d36ba7cfec
--- /dev/null
+++ b/packages/component-helper-service/index.js
@@ -0,0 +1 @@
+module.exports = require('./src/Helper')
diff --git a/packages/component-helper-service/package.json b/packages/component-helper-service/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..e4aa40e13b33df0be7f7a47e1c00a8ef7019a8b1
--- /dev/null
+++ b/packages/component-helper-service/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "pubsweet-component-helper-service",
+  "version": "0.0.1",
+  "description": "helper 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-faraday",
+    "path": "component-helper-service"
+  },
+  "dependencies": {
+  },
+  "peerDependencies": {
+    "@pubsweet/logger": "^0.0.1",
+    "pubsweet-server": "^1.0.1"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
diff --git a/packages/component-helper-service/src/Helper.js b/packages/component-helper-service/src/Helper.js
new file mode 100644
index 0000000000000000000000000000000000000000..0037b50c55ed4e8f5af9baccf34702df12b77731
--- /dev/null
+++ b/packages/component-helper-service/src/Helper.js
@@ -0,0 +1,19 @@
+const Email = require('./services/Email')
+const Collection = require('./services/Collection')
+const Fragment = require('./services/Fragment')
+const services = require('./services/services')
+const authsome = require('./services/authsome')
+const User = require('./services/User')
+const Team = require('./services/Team')
+const Invitation = require('./services/Invitation')
+
+module.exports = {
+  Email,
+  Collection,
+  Fragment,
+  services,
+  authsome,
+  User,
+  Team,
+  Invitation,
+}
diff --git a/packages/component-helper-service/src/services/Collection.js b/packages/component-helper-service/src/services/Collection.js
new file mode 100644
index 0000000000000000000000000000000000000000..6e502735b7ae9ce777bbda52be940b4e06abf953
--- /dev/null
+++ b/packages/component-helper-service/src/services/Collection.js
@@ -0,0 +1,107 @@
+const config = require('config')
+
+const statuses = config.get('statuses')
+
+class Collection {
+  constructor({ collection = {} }) {
+    this.collection = collection
+  }
+
+  async updateStatusByRecommendation({ recommendation }) {
+    let newStatus = 'pendingApproval'
+    if (['minor', 'major'].includes(recommendation))
+      newStatus = 'revisionRequested'
+
+    await this.updateStatus({ newStatus })
+  }
+
+  async updateFinalStatusByRecommendation({ recommendation }) {
+    let newStatus
+    switch (recommendation) {
+      case 'reject':
+        newStatus = 'rejected'
+        break
+      case 'publish':
+        newStatus = 'published'
+        break
+      case 'return-to-handling-editor':
+        newStatus = 'reviewCompleted'
+        break
+      default:
+        break
+    }
+
+    await this.updateStatus({ newStatus })
+  }
+
+  async updateStatus({ newStatus }) {
+    this.collection.status = newStatus
+    this.collection.visibleStatus = statuses[this.collection.status].private
+    await this.collection.save()
+  }
+
+  async getAuthorData({ UserModel }) {
+    const { collection: { authors } } = this
+    const submittingAuthorData = authors.find(
+      author => author.isSubmitting === true,
+    )
+    const submittingAuthor = await UserModel.find(submittingAuthorData.userId)
+    const authorsPromises = authors.map(async author => {
+      const user = await UserModel.find(author.userId)
+      return `${user.firstName} ${user.lastName}`
+    })
+    const authorsList = await Promise.all(authorsPromises)
+
+    return {
+      authorsList,
+      submittingAuthor,
+    }
+  }
+
+  getReviewerInvitations({ agree = true }) {
+    const { collection: { invitations } } = this
+    return agree
+      ? invitations.filter(
+          inv =>
+            inv.role === 'reviewer' &&
+            inv.hasAnswer === true &&
+            inv.isAccepted === true,
+        )
+      : invitations.filter(
+          inv => inv.role === 'reviewer' && inv.hasAnswer === false,
+        )
+  }
+
+  async addHandlingEditor({ user, invitation }) {
+    this.collection.handlingEditor = {
+      id: user.id,
+      name: `${user.firstName} ${user.lastName}`,
+      invitedOn: invitation.invitedOn,
+      respondedOn: invitation.respondedOn,
+      email: user.email,
+      hasAnswer: invitation.hasAnswer,
+      isAccepted: invitation.isAccepted,
+    }
+    await this.updateStatus({ newStatus: 'heInvited' })
+  }
+
+  async updateHandlingEditor({ isAccepted }) {
+    const { collection: { handlingEditor } } = this
+    handlingEditor.hasAnswer = true
+    handlingEditor.isAccepted = isAccepted
+    handlingEditor.respondedOn = Date.now()
+    let status
+    isAccepted ? (status = 'heAssigned') : (status = 'submitted')
+    await this.updateStatus({ newStatus: status })
+  }
+
+  async updateStatusByNumberOfReviewers() {
+    const reviewerInvitations = this.collection.invitations.filter(
+      inv => inv.role === 'reviewer',
+    )
+    if (reviewerInvitations.length === 0)
+      await this.updateStatus({ newStatus: 'heAssigned' })
+  }
+}
+
+module.exports = Collection
diff --git a/packages/component-manuscript-manager/src/helpers/Email.js b/packages/component-helper-service/src/services/Email.js
similarity index 68%
rename from packages/component-manuscript-manager/src/helpers/Email.js
rename to packages/component-helper-service/src/services/Email.js
index 044dc1797d147d1fa7f1909c1395b6e6e694fb25..49a106d8d72305dcf029accd2bd0a094a52cab45 100644
--- a/packages/component-manuscript-manager/src/helpers/Email.js
+++ b/packages/component-helper-service/src/services/Email.js
@@ -1,4 +1,5 @@
-const collectionHelper = require('./Collection')
+const Collection = require('./Collection')
+const User = require('./User')
 const get = require('lodash/get')
 const config = require('config')
 const mailService = require('pubsweet-component-mail-service')
@@ -31,19 +32,14 @@ class Email {
   }) {
     const {
       UserModel,
+      collection,
       parsedFragment: { recommendations, title, type },
       authors: { submittingAuthor: { firstName = '', lastName = '' } },
-      collection: {
-        customId,
-        invitations = [],
-        handlingEditor: { name: heName },
-      },
     } = this
-
-    const reviewerInvitations = collectionHelper.getReviewerInvitations(
-      invitations,
+    const collectionHelper = new Collection({ collection })
+    const reviewerInvitations = collectionHelper.getReviewerInvitations({
       agree,
-    )
+    })
 
     const hasReview = invUserId => rec =>
       rec.recommendationType === 'review' &&
@@ -59,15 +55,18 @@ class Email {
     })
     let emailType = 'agreed-reviewers-after-recommendation'
     let emailText, subject, manuscriptType
-    const eic = await getEditorInChief(UserModel)
-    const editorName = isSubmitted ? `${eic.firstName} ${eic.lastName}` : heName
+    const userHelper = new User({ UserModel })
+    const eic = await userHelper.getEditorInChief()
+    const editorName = isSubmitted
+      ? `${eic.firstName} ${eic.lastName}`
+      : collection.handlingEditor.name
 
     let reviewers = await Promise.all(reviewerPromises)
     reviewers = reviewers.filter(Boolean)
     if (agree) {
       subject = isSubmitted
-        ? `${customId}: Manuscript Decision`
-        : `${customId}: Manuscript ${getSubject(recommendation)}`
+        ? `${collection.customId}: Manuscript Decision`
+        : `${collection.customId}: Manuscript ${getSubject(recommendation)}`
 
       if (isSubmitted) {
         emailType = 'submitting-reviewers-after-decision'
@@ -75,7 +74,7 @@ class Email {
         if (recommendation === 'publish') emailText = 'will now be published'
       }
     } else {
-      subject = `${customId}: Reviewer Unassigned`
+      subject = `${collection.customId}: Reviewer Unassigned`
       manuscriptType = manuscriptTypes[type]
       emailType = 'no-response-reviewers-after-recommendation'
     }
@@ -161,7 +160,8 @@ class Email {
       parsedFragment: { title, id },
       authors: { submittingAuthor: { firstName = '', lastName = '' } },
     } = this
-    const eic = await getEditorInChief(UserModel)
+    const userHelper = new User({ UserModel })
+    const eic = await userHelper.getEditorInChief()
     const toEmail = collection.handlingEditor.email
     let emailType = publish
       ? 'he-manuscript-published'
@@ -222,7 +222,8 @@ class Email {
         throw new Error('undefined HE recommentation type')
     }
 
-    const eic = await getEditorInChief(UserModel)
+    const userHelper = new User({ UserModel })
+    const eic = await userHelper.getEditorInChief()
     const toEmail = eic.email
     mailService.sendNotificationEmail({
       toEmail,
@@ -237,6 +238,103 @@ class Email {
       },
     })
   }
+
+  setupReviewerInvitationEmail({
+    user,
+    invitationId,
+    timestamp,
+    resend = false,
+    authorName,
+  }) {
+    const {
+      baseUrl,
+      collection,
+      parsedFragment: { id, title, abstract },
+      authors,
+    } = this
+    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 userHelper = new User({ UserModel })
+    const eic = await userHelper.getEditorInChief()
+    const toEmail = collection.handlingEditor.email
+    mailService.sendNotificationEmail({
+      toEmail,
+      user,
+      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({
+        toEmail: user.email,
+        user,
+        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, fragment: { title } } = this
+
+    await mailService.sendNotificationEmail({
+      toEmail: user.email,
+      user,
+      emailType: 'unassign-reviewer',
+      meta: {
+        collection: { customId: collection.customId },
+        fragment: { title, authorName },
+        handlingEditorName: collection.handlingEditor.name,
+      },
+    })
+  }
 }
 
 const getSubject = recommendation =>
@@ -252,10 +350,4 @@ const getHeRecommendation = recommendation => {
   return heRecommendation
 }
 
-const getEditorInChief = async UserModel => {
-  const users = await UserModel.all()
-  const eic = users.find(user => user.editorInChief || user.admin)
-  return eic
-}
-
 module.exports = Email
diff --git a/packages/component-helper-service/src/services/Fragment.js b/packages/component-helper-service/src/services/Fragment.js
new file mode 100644
index 0000000000000000000000000000000000000000..d4be48eec23c41fd8e08e2d5695a34a705454fb1
--- /dev/null
+++ b/packages/component-helper-service/src/services/Fragment.js
@@ -0,0 +1,26 @@
+class Fragment {
+  constructor({ fragment }) {
+    this.fragment = fragment
+  }
+  async getFragmentData({ handlingEditor = {} }) {
+    const { fragment: { metadata, recommendations = [], id } } = this
+    const heRecommendation = recommendations.find(
+      rec => rec.userId === handlingEditor.id,
+    )
+    let { title, abstract } = metadata
+    const { type } = metadata
+    title = title.replace(/<(.|\n)*?>/g, '')
+    abstract = abstract ? abstract.replace(/<(.|\n)*?>/g, '') : ''
+
+    return {
+      id,
+      type,
+      title,
+      abstract,
+      recommendations,
+      heRecommendation,
+    }
+  }
+}
+
+module.exports = Fragment
diff --git a/packages/component-helper-service/src/services/Invitation.js b/packages/component-helper-service/src/services/Invitation.js
new file mode 100644
index 0000000000000000000000000000000000000000..ac595dabf0ee0f4880a343f29c8c4376c171a1e6
--- /dev/null
+++ b/packages/component-helper-service/src/services/Invitation.js
@@ -0,0 +1,61 @@
+const uuid = require('uuid')
+const logger = require('@pubsweet/logger')
+
+class Invitation {
+  constructor({ userId, role }) {
+    this.userId = userId
+    this.role = role
+  }
+
+  set _userId(newUserId) {
+    this.userId = newUserId
+  }
+
+  getInvitationsData({ invitations = [] }) {
+    const { userId, role } = this
+    const matchingInvitation = invitations.find(
+      inv => inv.role === role && inv.userId === userId,
+    )
+    if (!matchingInvitation) {
+      logger.error(
+        `There should be at least one matching invitation between User ${userId} and Role ${role}`,
+      )
+      throw Error('no matching invitation')
+    }
+    let status = 'pending'
+    if (matchingInvitation.isAccepted) {
+      status = 'accepted'
+    } else if (matchingInvitation.hasAnswer) {
+      status = 'declined'
+    }
+
+    const { invitedOn, respondedOn, id } = matchingInvitation
+    return { invitedOn, respondedOn, status, id }
+  }
+
+  async setupInvitation({ collection }) {
+    const { userId, role } = this
+    const invitation = {
+      role,
+      hasAnswer: false,
+      isAccepted: false,
+      invitedOn: Date.now(),
+      id: uuid.v4(),
+      userId,
+      respondedOn: null,
+    }
+    collection.invitations = collection.invitations || []
+    collection.invitations.push(invitation)
+    collection = await collection.save()
+    return invitation
+  }
+
+  getInvitation({ invitations = [] }) {
+    return invitations.find(
+      invitation =>
+        invitation.userId === this.userId && invitation.role === this.role,
+    )
+  }
+}
+
+module.exports = Invitation
diff --git a/packages/component-helper-service/src/services/Team.js b/packages/component-helper-service/src/services/Team.js
new file mode 100644
index 0000000000000000000000000000000000000000..b5b70ee1eb1f4ecb467f985ada697602f1247811
--- /dev/null
+++ b/packages/component-helper-service/src/services/Team.js
@@ -0,0 +1,133 @@
+const logger = require('@pubsweet/logger')
+const get = require('lodash/get')
+
+class Team {
+  constructor({ TeamModel = {}, collectionId = '' }) {
+    this.TeamModel = TeamModel
+    this.collectionId = collectionId
+  }
+
+  async createNewTeam({ role, userId }) {
+    const { collectionId, TeamModel } = this
+    let permissions, group, name
+    switch (role) {
+      case 'handlingEditor':
+        permissions = 'handlingEditor'
+        group = 'handlingEditor'
+        name = 'Handling Editor'
+        break
+      case 'reviewer':
+        permissions = 'reviewer'
+        group = 'reviewer'
+        name = 'Reviewer'
+        break
+      case 'author':
+        permissions = 'author'
+        group = 'author'
+        name = 'author'
+        break
+      default:
+        break
+    }
+
+    const teamBody = {
+      teamType: {
+        name: role,
+        permissions,
+      },
+      group,
+      name,
+      object: {
+        type: 'collection',
+        id: collectionId,
+      },
+      members: [userId],
+    }
+    let team = new TeamModel(teamBody)
+    team = await team.save()
+    return team
+  }
+
+  async setupManuscriptTeam({ user, role }) {
+    const { TeamModel, collectionId } = this
+    const teams = await TeamModel.all()
+    user.teams = user.teams || []
+    let foundTeam = teams.find(
+      team =>
+        team.group === role &&
+        team.object.type === 'collection' &&
+        team.object.id === collectionId,
+    )
+
+    if (foundTeam !== undefined) {
+      if (!foundTeam.members.includes(user.id)) {
+        foundTeam.members.push(user.id)
+      }
+
+      try {
+        foundTeam = await foundTeam.save()
+        if (!user.teams.includes(foundTeam.id)) {
+          user.teams.push(foundTeam.id)
+          await user.save()
+        }
+        return foundTeam
+      } catch (e) {
+        logger.error(e)
+      }
+    } else {
+      const team = await this.createNewTeam({ role, userId: user.id })
+      user.teams.push(team.id)
+      await user.save()
+      return team
+    }
+  }
+
+  async removeTeamMember({ teamId, userId }) {
+    const { TeamModel } = this
+    const team = await TeamModel.find(teamId)
+    const members = team.members.filter(member => member !== userId)
+    team.members = members
+    await team.save()
+  }
+
+  async getTeamMembersByCollection({ role }) {
+    const { TeamModel, collectionId } = this
+
+    const teams = await TeamModel.all()
+    const members = get(
+      teams.find(
+        team =>
+          team.group === role &&
+          team.object.type === 'collection' &&
+          team.object.id === collectionId,
+      ),
+      'members',
+    )
+
+    return members
+  }
+
+  async getTeamByGroupAndCollection({ role }) {
+    const { TeamModel, collectionId } = this
+    const teams = await TeamModel.all()
+    return teams.find(
+      team =>
+        team.group === role &&
+        team.object.type === 'collection' &&
+        team.object.id === collectionId,
+    )
+  }
+
+  async updateHETeam({ collection, role, user }) {
+    const team = await this.getTeamByGroupAndCollection({
+      role,
+    })
+    delete collection.handlingEditor
+    await this.removeTeamMember({ teamId: team.id, userId: user.id })
+    user.teams = user.teams.filter(userTeamId => team.id !== userTeamId)
+    await user.save()
+    await collection.save()
+  }
+}
+
+module.exports = Team
diff --git a/packages/component-helper-service/src/services/User.js b/packages/component-helper-service/src/services/User.js
new file mode 100644
index 0000000000000000000000000000000000000000..ed69cd655e6acd1445dacab855d2ddbde74dd7b9
--- /dev/null
+++ b/packages/component-helper-service/src/services/User.js
@@ -0,0 +1,70 @@
+const uuid = require('uuid')
+const crypto = require('crypto')
+const logger = require('@pubsweet/logger')
+const mailService = require('pubsweet-component-mail-service')
+
+class User {
+  constructor({ UserModel = {} }) {
+    this.UserModel = UserModel
+  }
+
+  async createUser({ role, body }) {
+    const { UserModel } = this
+    const { email, firstName, lastName, affiliation, title } = body
+    const username = email
+    const password = uuid.v4()
+    const userBody = {
+      username,
+      email,
+      password,
+      passwordResetToken: crypto.randomBytes(32).toString('hex'),
+      isConfirmed: false,
+      firstName,
+      lastName,
+      affiliation,
+      title,
+      editorInChief: role === 'editorInChief',
+      admin: role === 'admin',
+      handlingEditor: role === 'handlingEditor',
+      invitationToken: role === 'reviewer' ? uuid.v4() : '',
+    }
+
+    let newUser = new UserModel(userBody)
+
+    try {
+      newUser = await newUser.save()
+      return newUser
+    } catch (e) {
+      logger.error(e)
+    }
+  }
+
+  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
+  }
+}
+
+module.exports = User
diff --git a/packages/component-invite/src/helpers/authsome.js b/packages/component-helper-service/src/services/authsome.js
similarity index 100%
rename from packages/component-invite/src/helpers/authsome.js
rename to packages/component-helper-service/src/services/authsome.js
diff --git a/packages/component-manuscript-manager/src/helpers/helpers.js b/packages/component-helper-service/src/services/services.js
similarity index 96%
rename from packages/component-manuscript-manager/src/helpers/helpers.js
rename to packages/component-helper-service/src/services/services.js
index a37f7504aaa1efd3fbacdc94734fc0807cfa5734..cb56e8ec7edd3c811f3f77f11f66567c70d4a898 100644
--- a/packages/component-manuscript-manager/src/helpers/helpers.js
+++ b/packages/component-helper-service/src/services/services.js
@@ -8,7 +8,7 @@ const checkForUndefinedParams = (...params) => {
   return true
 }
 
-const validateEmailAndToken = async (email, token, userModel) => {
+const validateEmailAndToken = async ({ email, token, userModel }) => {
   try {
     const user = await userModel.findByEmail(email)
     if (user) {
diff --git a/packages/component-invite/config/default.js b/packages/component-invite/config/default.js
index 7afbb2f22c26d8ae4d9f314989bc4a02189360f7..4609e678ed2d63b591dde366b05826f85b78d835 100644
--- a/packages/component-invite/config/default.js
+++ b/packages/component-invite/config/default.js
@@ -55,4 +55,16 @@ module.exports = {
       private: 'Under Review',
     },
   },
+  'manuscript-types': {
+    research: 'Research',
+    review: 'Review',
+    'clinical-study': 'Clinical Study',
+    'case-report': 'Case Report',
+    'letter-to-editor': 'Letter to the Editor',
+    editorial: 'Editorial',
+    corrigendum: 'Corrigendum',
+    erratum: 'Erratum',
+    'expression-of-concern': 'Expression of Concern',
+    retraction: 'Retraction',
+  },
 }
diff --git a/packages/component-invite/config/test.js b/packages/component-invite/config/test.js
index 0eb54780a931f66f1b611d9a4d51552908ece2c3..e622200e004081d3a576664b5547179e87fede3c 100644
--- a/packages/component-invite/config/test.js
+++ b/packages/component-invite/config/test.js
@@ -56,4 +56,16 @@ module.exports = {
       private: 'Under Review',
     },
   },
+  'manuscript-types': {
+    research: 'Research',
+    review: 'Review',
+    'clinical-study': 'Clinical Study',
+    'case-report': 'Case Report',
+    'letter-to-editor': 'Letter to the Editor',
+    editorial: 'Editorial',
+    corrigendum: 'Corrigendum',
+    erratum: 'Erratum',
+    'expression-of-concern': 'Expression of Concern',
+    retraction: 'Retraction',
+  },
 }
diff --git a/packages/component-invite/src/helpers/Collection.js b/packages/component-invite/src/helpers/Collection.js
deleted file mode 100644
index d04841fc244fa3b7619bacd064d3685267d42ccd..0000000000000000000000000000000000000000
--- a/packages/component-invite/src/helpers/Collection.js
+++ /dev/null
@@ -1,72 +0,0 @@
-const config = require('config')
-const last = require('lodash/last')
-
-const statuses = config.get('statuses')
-
-const addHandlingEditor = async (collection, user, invitation) => {
-  collection.handlingEditor = {
-    id: user.id,
-    name: `${user.firstName} ${user.lastName}`,
-    invitedOn: invitation.invitedOn,
-    respondedOn: invitation.respondedOn,
-    email: user.email,
-    hasAnswer: invitation.hasAnswer,
-    isAccepted: invitation.isAccepted,
-  }
-  await updateStatus(collection, 'heInvited')
-}
-
-const updateHandlingEditor = async (collection, isAccepted) => {
-  collection.handlingEditor.hasAnswer = true
-  collection.handlingEditor.isAccepted = isAccepted
-  collection.handlingEditor.respondedOn = Date.now()
-  let status
-  isAccepted ? (status = 'heAssigned') : (status = 'submitted')
-  await updateStatus(collection, status)
-}
-
-const getFragmentAndAuthorData = async ({
-  UserModel,
-  FragmentModel,
-  collection: { fragments, authors },
-}) => {
-  const fragment = await FragmentModel.find(last(fragments))
-  let { title, abstract } = fragment.metadata
-  title = title.replace(/<(.|\n)*?>/g, '')
-  abstract = abstract ? abstract.replace(/<(.|\n)*?>/g, '') : ''
-
-  const submittingAuthorData = authors.find(
-    author => author.isSubmitting === true,
-  )
-  const author = await UserModel.find(submittingAuthorData.userId)
-  const authorName = `${author.firstName} ${author.lastName}`
-  const authorsPromises = authors.map(async author => {
-    const user = await UserModel.find(author.userId)
-    return ` ${user.firstName} ${user.lastName}`
-  })
-  const authorsList = await Promise.all(authorsPromises)
-  const { id } = fragment
-  return { title, authorName, id, abstract, authorsList }
-}
-
-const updateReviewerCollectionStatus = async collection => {
-  const reviewerInvitations = collection.invitations.filter(
-    inv => inv.role === 'reviewer',
-  )
-  if (reviewerInvitations.length === 0)
-    await updateStatus(collection, 'heAssigned')
-}
-
-const updateStatus = async (collection, newStatus) => {
-  collection.status = newStatus
-  collection.visibleStatus = statuses[collection.status].private
-  await collection.save()
-}
-
-module.exports = {
-  addHandlingEditor,
-  updateHandlingEditor,
-  getFragmentAndAuthorData,
-  updateReviewerCollectionStatus,
-  updateStatus,
-}
diff --git a/packages/component-invite/src/helpers/Invitation.js b/packages/component-invite/src/helpers/Invitation.js
deleted file mode 100644
index c543f1f5435d0822a649c96144f14bfa5f93ea1b..0000000000000000000000000000000000000000
--- a/packages/component-invite/src/helpers/Invitation.js
+++ /dev/null
@@ -1,95 +0,0 @@
-const uuid = require('uuid')
-const collectionHelper = require('./Collection')
-
-const getInvitationData = (invitations, userId, role) => {
-  const matchingInvitation = invitations.find(
-    inv => inv.role === role && inv.userId === userId,
-  )
-  let status = 'pending'
-  if (matchingInvitation.isAccepted) {
-    status = 'accepted'
-  } else if (matchingInvitation.hasAnswer) {
-    status = 'declined'
-  }
-
-  const { invitedOn, respondedOn, id } = matchingInvitation
-  return { invitedOn, respondedOn, status, id }
-}
-
-const setupInvitation = async (userId, role, collection) => {
-  const invitation = {
-    role,
-    hasAnswer: false,
-    isAccepted: false,
-    invitedOn: Date.now(),
-    id: uuid.v4(),
-    userId,
-    respondedOn: null,
-  }
-  collection.invitations = collection.invitations || []
-  collection.invitations.push(invitation)
-  collection = await collection.save()
-  return invitation
-}
-
-const setupReviewerInvitation = async ({
-  baseUrl,
-  FragmentModel,
-  UserModel,
-  collection,
-  user,
-  mailService,
-  invitationId,
-  timestamp,
-  resend = false,
-}) => {
-  const {
-    title,
-    authorName,
-    id,
-    abstract,
-    authorsList,
-  } = await collectionHelper.getFragmentAndAuthorData({
-    UserModel,
-    FragmentModel,
-    collection,
-  })
-
-  const params = {
-    user,
-    baseUrl,
-    subject: `${collection.customId}: Review Requested`,
-    toEmail: user.email,
-    meta: {
-      fragment: {
-        id,
-        title,
-        abstract,
-        authors: authorsList,
-      },
-      invitation: {
-        id: invitationId,
-        timestamp,
-      },
-      collection: {
-        id: collection.id,
-        authorName,
-        handlingEditor: collection.handlingEditor,
-      },
-    },
-    emailType: resend ? 'resend-reviewer' : 'invite-reviewer',
-  }
-  await mailService.sendReviewerInvitationEmail(params)
-}
-
-const getInvitation = (invitations = [], userId, role) =>
-  invitations.find(
-    invitation => invitation.userId === userId && invitation.role === role,
-  )
-
-module.exports = {
-  getInvitationData,
-  setupInvitation,
-  setupReviewerInvitation,
-  getInvitation,
-}
diff --git a/packages/component-invite/src/helpers/Team.js b/packages/component-invite/src/helpers/Team.js
deleted file mode 100644
index 8092336a8cdd94720eea378cd4dfae14a3453f7f..0000000000000000000000000000000000000000
--- a/packages/component-invite/src/helpers/Team.js
+++ /dev/null
@@ -1,125 +0,0 @@
-const logger = require('@pubsweet/logger')
-const get = require('lodash/get')
-
-const createNewTeam = async (collectionId, role, userId, TeamModel) => {
-  let permissions, group, name
-  switch (role) {
-    case 'handlingEditor':
-      permissions = 'handlingEditor'
-      group = 'handlingEditor'
-      name = 'Handling Editor'
-      break
-    case 'reviewer':
-      permissions = 'reviewer'
-      group = 'reviewer'
-      name = 'Reviewer'
-      break
-    case 'author':
-      permissions = 'author'
-      group = 'author'
-      name = 'author'
-      break
-    default:
-      break
-  }
-
-  const teamBody = {
-    teamType: {
-      name: role,
-      permissions,
-    },
-    group,
-    name,
-    object: {
-      type: 'collection',
-      id: collectionId,
-    },
-    members: [userId],
-  }
-  let team = new TeamModel(teamBody)
-  team = await team.save()
-  return team
-}
-
-const setupManuscriptTeam = async (models, user, collectionId, role) => {
-  const teams = await models.Team.all()
-  user.teams = user.teams || []
-  let foundTeam = teams.find(
-    team =>
-      team.group === role &&
-      team.object.type === 'collection' &&
-      team.object.id === collectionId,
-  )
-
-  if (foundTeam !== undefined) {
-    if (!foundTeam.members.includes(user.id)) {
-      foundTeam.members.push(user.id)
-    }
-
-    try {
-      foundTeam = await foundTeam.save()
-      if (!user.teams.includes(foundTeam.id)) {
-        user.teams.push(foundTeam.id)
-        await user.save()
-      }
-      return foundTeam
-    } catch (e) {
-      logger.error(e)
-    }
-  } else {
-    const team = await createNewTeam(collectionId, role, user.id, models.Team)
-    user.teams.push(team.id)
-    await user.save()
-    return team
-  }
-}
-
-const removeTeamMember = async (teamId, userId, TeamModel) => {
-  const team = await TeamModel.find(teamId)
-  const members = team.members.filter(member => member !== userId)
-  team.members = members
-  await team.save()
-}
-
-const getTeamMembersByCollection = async (collectionId, role, TeamModel) => {
-  const teams = await TeamModel.all()
-  const members = get(
-    teams.find(
-      team =>
-        team.group === role &&
-        team.object.type === 'collection' &&
-        team.object.id === collectionId,
-    ),
-    'members',
-  )
-
-  return members
-}
-
-const getTeamByGroupAndCollection = async (collectionId, role, TeamModel) => {
-  const teams = await TeamModel.all()
-  return teams.find(
-    team =>
-      team.group === role &&
-      team.object.type === 'collection' &&
-      team.object.id === collectionId,
-  )
-}
-
-const updateHETeam = async (collection, role, TeamModel, user) => {
-  const team = await getTeamByGroupAndCollection(collection.id, role, TeamModel)
-  delete collection.handlingEditor
-  await removeTeamMember(team.id, user.id, TeamModel)
-  user.teams = user.teams.filter(userTeamId => team.id !== userTeamId)
-  await user.save()
-  await collection.save()
-}
-
-module.exports = {
-  createNewTeam,
-  setupManuscriptTeam,
-  removeTeamMember,
-  getTeamMembersByCollection,
-  getTeamByGroupAndCollection,
-  updateHETeam,
-}
diff --git a/packages/component-invite/src/helpers/User.js b/packages/component-invite/src/helpers/User.js
deleted file mode 100644
index 4b293a2ad476f3ccf4269869234985b7b6dc0c05..0000000000000000000000000000000000000000
--- a/packages/component-invite/src/helpers/User.js
+++ /dev/null
@@ -1,129 +0,0 @@
-const helpers = require('./helpers')
-const mailService = require('pubsweet-component-mail-service')
-const logger = require('@pubsweet/logger')
-const collectionHelper = require('./Collection')
-
-const setupNewUser = async (
-  body,
-  url,
-  res,
-  email,
-  role,
-  UserModel,
-  invitationType,
-) => {
-  const { firstName, lastName, affiliation, title } = body
-  const newUser = await helpers.createNewUser(
-    email,
-    firstName,
-    lastName,
-    affiliation,
-    title,
-    UserModel,
-    role,
-  )
-
-  try {
-    if (role !== 'reviewer') {
-      await 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.' }
-  }
-}
-
-const getEditorInChief = async UserModel => {
-  const users = await UserModel.all()
-  const eic = users.find(user => user.editorInChief || user.admin)
-  return eic
-}
-
-const setupReviewerDecisionEmailData = async ({
-  baseUrl,
-  UserModel,
-  FragmentModel,
-  collection,
-  user,
-  mailService,
-  agree,
-  timestamp = Date.now(),
-}) => {
-  const {
-    title,
-    authorName,
-    id,
-  } = await collectionHelper.getFragmentAndAuthorData({
-    UserModel,
-    FragmentModel,
-    collection,
-  })
-  const eic = await getEditorInChief(UserModel)
-  const toEmail = collection.handlingEditor.email
-  await mailService.sendNotificationEmail({
-    toEmail,
-    user,
-    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)
-    await mailService.sendNotificationEmail({
-      toEmail: user.email,
-      user,
-      emailType: 'reviewer-thank-you',
-      meta: {
-        collection: { customId: collection.customId, id: collection.id },
-        fragment: { id, title, authorName },
-        handlingEditorName: collection.handlingEditor.name,
-        baseUrl,
-      },
-    })
-}
-
-const setupReviewerUnassignEmail = async ({
-  UserModel,
-  FragmentModel,
-  collection,
-  user,
-  mailService,
-}) => {
-  const { title, authorName } = await collectionHelper.getFragmentAndAuthorData(
-    {
-      UserModel,
-      FragmentModel,
-      collection,
-    },
-  )
-
-  await mailService.sendNotificationEmail({
-    toEmail: user.email,
-    user,
-    emailType: 'unassign-reviewer',
-    meta: {
-      collection: { customId: collection.customId },
-      fragment: { title, authorName },
-      handlingEditorName: collection.handlingEditor.name,
-    },
-  })
-}
-
-module.exports = {
-  setupNewUser,
-  getEditorInChief,
-  setupReviewerDecisionEmailData,
-  setupReviewerUnassignEmail,
-}
diff --git a/packages/component-invite/src/helpers/helpers.js b/packages/component-invite/src/helpers/helpers.js
deleted file mode 100644
index b484721469b65e466195b899a64c3a0c40bed8da..0000000000000000000000000000000000000000
--- a/packages/component-invite/src/helpers/helpers.js
+++ /dev/null
@@ -1,123 +0,0 @@
-const logger = require('@pubsweet/logger')
-const uuid = require('uuid')
-const crypto = require('crypto')
-
-const checkForUndefinedParams = (...params) => {
-  if (params.includes(undefined)) {
-    return false
-  }
-
-  return true
-}
-
-const validateEmailAndToken = async (email, token, userModel) => {
-  try {
-    const user = await userModel.findByEmail(email)
-    if (user) {
-      if (token !== user.passwordResetToken) {
-        logger.error(
-          `invite pw reset tokens do not match: REQ ${token} vs. DB ${
-            user.passwordResetToken
-          }`,
-        )
-        return {
-          success: false,
-          status: 400,
-          message: 'invalid request',
-        }
-      }
-      return { success: true, user }
-    }
-  } catch (e) {
-    if (e.name === 'NotFoundError') {
-      logger.error('invite pw reset on non-existing user')
-      return {
-        success: false,
-        status: 404,
-        message: 'user not found',
-      }
-    } else if (e.name === 'ValidationError') {
-      logger.error('invite pw reset validation error')
-      return {
-        success: false,
-        status: 400,
-        message: e.details[0].message,
-      }
-    }
-    logger.error('internal server error')
-    return {
-      success: false,
-      status: 500,
-      message: e.details[0].message,
-    }
-  }
-  return {
-    success: false,
-    status: 500,
-    message: 'something went wrong',
-  }
-}
-
-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
-}
-
-const createNewUser = async (
-  email,
-  firstName,
-  lastName,
-  affiliation,
-  title,
-  UserModel,
-  role,
-) => {
-  const username = email
-  const password = uuid.v4()
-  const userBody = {
-    username,
-    email,
-    password,
-    passwordResetToken: crypto.randomBytes(32).toString('hex'),
-    isConfirmed: false,
-    firstName,
-    lastName,
-    affiliation,
-    title,
-    editorInChief: role === 'editorInChief',
-    admin: role === 'admin',
-    handlingEditor: role === 'handlingEditor',
-    invitationToken: role === 'reviewer' ? uuid.v4() : '',
-  }
-
-  let newUser = new UserModel(userBody)
-
-  try {
-    newUser = await newUser.save()
-    return newUser
-  } catch (e) {
-    logger.error(e)
-  }
-}
-
-const getBaseUrl = req => `${req.protocol}://${req.get('host')}`
-
-module.exports = {
-  checkForUndefinedParams,
-  validateEmailAndToken,
-  handleNotFoundError,
-  createNewUser,
-  getBaseUrl,
-}
diff --git a/packages/component-invite/src/routes/collectionsInvitations/decline.js b/packages/component-invite/src/routes/collectionsInvitations/decline.js
index fc88ffe4f4b69da2bf0481db895e247b90864b49..b049a4a0aeb43f096b0ec673b338d91202440cc4 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/decline.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/decline.js
@@ -1,16 +1,22 @@
-const helpers = require('../../helpers/helpers')
-const mailService = require('pubsweet-component-mail-service')
-const userHelper = require('../../helpers/User')
+const last = require('lodash/last')
+
+const {
+  services,
+  Email,
+  Fragment,
+  Collection,
+} = require('pubsweet-component-helper-service')
 
 module.exports = models => async (req, res) => {
   const { collectionId, invitationId } = req.params
   const { invitationToken } = req.body
 
-  if (!helpers.checkForUndefinedParams(invitationToken))
+  if (!services.checkForUndefinedParams(invitationToken))
     return res.status(400).json({ error: 'Token is required' })
 
+  const UserModel = models.User
   try {
-    const user = await models.User.findOneByField(
+    const user = await UserModel.findOneByField(
       'invitationToken',
       invitationToken,
     )
@@ -37,18 +43,32 @@ module.exports = models => async (req, res) => {
     invitation.hasAnswer = true
     invitation.isAccepted = false
     await collection.save()
-    return await userHelper.setupReviewerDecisionEmailData({
-      baseUrl: helpers.getBaseUrl(req),
-      UserModel: models.User,
-      FragmentModel: models.Fragment,
+    const collectionHelper = new Collection({ collection })
+    const fragment = await models.Fragment.find(last(collection.fragments))
+    const fragmentHelper = new Fragment({ fragment })
+    const parsedFragment = await fragmentHelper.getFragmentData({
+      handlingEditor: collection.handlingEditor,
+    })
+    const baseUrl = services.getBaseUrl(req)
+    const {
+      authorsList: authors,
+      submittingAuthor,
+    } = await collectionHelper.getAuthorData({ UserModel })
+    const emailHelper = new Email({
+      UserModel,
       collection,
-      reviewerName: `${user.firstName} ${user.lastName}`,
-      mailService,
+      parsedFragment,
+      baseUrl,
+      authors,
+    })
+    emailHelper.setupReviewerDecisionEmail({
+      authorName: `${submittingAuthor.firstName} ${submittingAuthor.lastName}`,
       agree: false,
       user,
     })
+    return res.status(200).json({})
   } catch (e) {
-    const notFoundError = await helpers.handleNotFoundError(e, 'item')
+    const notFoundError = await services.handleNotFoundError(e, 'item')
     return res.status(notFoundError.status).json({
       error: notFoundError.message,
     })
diff --git a/packages/component-invite/src/routes/collectionsInvitations/delete.js b/packages/component-invite/src/routes/collectionsInvitations/delete.js
index d1640d5d78afd6aab05dbd8aa74c13f3c150bbeb..e3e69c0ecc26e772b0428004df9fa9134010d141 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/delete.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/delete.js
@@ -1,17 +1,25 @@
-const helpers = require('../../helpers/helpers')
-const teamHelper = require('../../helpers/Team')
-const mailService = require('pubsweet-component-mail-service')
-const logger = require('@pubsweet/logger')
 const config = require('config')
-const userHelper = require('../../helpers/User')
-const collectionHelper = require('../../helpers/Collection')
-const authsomeHelper = require('../../helpers/authsome')
+const logger = require('@pubsweet/logger')
+const last = require('lodash/last')
+const mailService = require('pubsweet-component-mail-service')
+
+const {
+  services,
+  Team,
+  Email,
+  Fragment,
+  Collection,
+  authsome: authsomeHelper,
+} = require('pubsweet-component-helper-service')
 
 const statuses = config.get('statuses')
 module.exports = models => async (req, res) => {
   const { collectionId, invitationId } = req.params
+  const teamHelper = new Team({ TeamModel: models.Team, collectionId })
+
   try {
     const collection = await models.Collection.find(collectionId)
+    const collectionHelper = new Collection({ collection })
     const authsome = authsomeHelper.getAuthsome(models)
     const target = {
       collection,
@@ -31,11 +39,10 @@ module.exports = models => async (req, res) => {
       })
       return
     }
-    const team = await teamHelper.getTeamByGroupAndCollection(
-      collectionId,
-      invitation.role,
-      models.Team,
-    )
+
+    const team = await teamHelper.getTeamByGroupAndCollection({
+      role: invitation.role,
+    })
 
     collection.invitations = collection.invitations.filter(
       inv => inv.id !== invitation.id,
@@ -45,11 +52,15 @@ module.exports = models => async (req, res) => {
       collection.visibleStatus = statuses[collection.status].private
       delete collection.handlingEditor
     } else if (invitation.role === 'reviewer') {
-      await collectionHelper.updateReviewerCollectionStatus(collection)
+      await collectionHelper.updateStatusByNumberOfReviewers()
     }
     await collection.save()
-    await teamHelper.removeTeamMember(team.id, invitation.userId, models.Team)
-    const user = await models.User.find(invitation.userId)
+    await teamHelper.removeTeamMember({
+      teamId: team.id,
+      userId: invitation.userId,
+    })
+    const UserModel = models.User
+    const user = await UserModel.find(invitation.userId)
     user.teams = user.teams.filter(userTeamId => team.id !== userTeamId)
     await user.save()
     try {
@@ -59,12 +70,29 @@ module.exports = models => async (req, res) => {
           emailType: 'revoke-handling-editor',
         })
       } else if (invitation.role === 'reviewer') {
-        await userHelper.setupReviewerUnassignEmail({
-          UserModel: models.User,
-          FragmentModel: models.Fragment,
+        const collectionHelper = new Collection({ collection })
+        const fragment = await models.Fragment.find(last(collection.fragments))
+        const fragmentHelper = new Fragment({ fragment })
+        const parsedFragment = await fragmentHelper.getFragmentData({
+          handlingEditor: collection.handlingEditor,
+        })
+        const baseUrl = services.getBaseUrl(req)
+        const {
+          authorsList: authors,
+          submittingAuthor,
+        } = await collectionHelper.getAuthorData({ UserModel })
+        const emailHelper = new Email({
+          UserModel,
           collection,
+          parsedFragment,
+          baseUrl,
+          authors,
+        })
+        emailHelper.setupReviewerUnassignEmail({
           user,
-          mailService,
+          authorName: `${submittingAuthor.firstName} ${
+            submittingAuthor.lastName
+          }`,
         })
       }
 
@@ -74,7 +102,7 @@ module.exports = models => async (req, res) => {
       return res.status(500).json({ error: 'Email could not be sent.' })
     }
   } catch (e) {
-    const notFoundError = await helpers.handleNotFoundError(e, 'collection')
+    const notFoundError = await services.handleNotFoundError(e, 'collection')
     return res.status(notFoundError.status).json({
       error: notFoundError.message,
     })
diff --git a/packages/component-invite/src/routes/collectionsInvitations/get.js b/packages/component-invite/src/routes/collectionsInvitations/get.js
index 308a57670a9030a82833f445fef7855083077edb..18e1e0eb7a8a8c08a0fb5f3c34e220c91c1678c7 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/get.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/get.js
@@ -1,13 +1,16 @@
-const helpers = require('../../helpers/helpers')
-const teamHelper = require('../../helpers/Team')
 const config = require('config')
-const invitationHelper = require('../../helpers/Invitation')
-const authsomeHelper = require('../../helpers/authsome')
+const {
+  services,
+  Team,
+  Invitation,
+  authsome: authsomeHelper,
+} = require('pubsweet-component-helper-service')
 
 const configRoles = config.get('roles')
+
 module.exports = models => async (req, res) => {
   const { role } = req.query
-  if (!helpers.checkForUndefinedParams(role)) {
+  if (!services.checkForUndefinedParams(role)) {
     res.status(400).json({ error: 'Role is required' })
     return
   }
@@ -16,7 +19,10 @@ module.exports = models => async (req, res) => {
     res.status(400).json({ error: `Role ${role} is invalid` })
     return
   }
+
   const { collectionId } = req.params
+  const teamHelper = new Team({ TeamModel: models.Team, collectionId })
+
   try {
     const collection = await models.Collection.find(collectionId)
     const authsome = authsomeHelper.getAuthsome(models)
@@ -25,31 +31,31 @@ module.exports = models => async (req, res) => {
       path: req.route.path,
     }
     const canGet = await authsome.can(req.user, 'GET', target)
+
     if (!canGet)
       return res.status(403).json({
         error: 'Unauthorized.',
       })
-    const members = await teamHelper.getTeamMembersByCollection(
-      collectionId,
-      role,
-      models.Team,
-    )
 
+    const members = await teamHelper.getTeamMembersByCollection({
+      role,
+    })
     if (members === undefined) return res.status(200).json([])
 
     // TO DO: handle case for when the invitationID is provided
     const membersData = members.map(async member => {
       const user = await models.User.find(member)
+      const invitationHelper = new Invitation({ userId: user.id, role })
+
       const {
         invitedOn,
         respondedOn,
         status,
         id,
-      } = invitationHelper.getInvitationData(
-        collection.invitations,
-        user.id,
-        role,
-      )
+      } = invitationHelper.getInvitationsData({
+        invitations: collection.invitations,
+      })
+
       return {
         name: `${user.firstName} ${user.lastName}`,
         invitedOn,
@@ -64,7 +70,7 @@ module.exports = models => async (req, res) => {
     const resBody = await Promise.all(membersData)
     res.status(200).json(resBody)
   } catch (e) {
-    const notFoundError = await helpers.handleNotFoundError(e, 'collection')
+    const notFoundError = await services.handleNotFoundError(e, 'collection')
     return res.status(notFoundError.status).json({
       error: notFoundError.message,
     })
diff --git a/packages/component-invite/src/routes/collectionsInvitations/patch.js b/packages/component-invite/src/routes/collectionsInvitations/patch.js
index 657ba2794843e4e5df353e2b52f26de5b85161f1..69d6302f6184d69ead39341d5867748bec23477d 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/patch.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/patch.js
@@ -1,20 +1,27 @@
 const logger = require('@pubsweet/logger')
-const helpers = require('../../helpers/helpers')
-const teamHelper = require('../../helpers/Team')
 const mailService = require('pubsweet-component-mail-service')
-const userHelper = require('../../helpers/User')
-const collectionHelper = require('../../helpers/Collection')
+const last = require('lodash/last')
+
+const {
+  Email,
+  services,
+  Fragment,
+  Collection,
+  Team,
+  User,
+} = require('pubsweet-component-helper-service')
 
 module.exports = models => async (req, res) => {
   const { collectionId, invitationId } = req.params
   const { isAccepted, reason } = req.body
 
-  if (!helpers.checkForUndefinedParams(isAccepted)) {
+  if (!services.checkForUndefinedParams(isAccepted)) {
     res.status(400).json({ error: 'Missing parameters.' })
     logger.error('some parameters are missing')
     return
   }
-  let user = await models.User.find(req.user)
+  const UserModel = models.User
+  const user = await UserModel.find(req.user)
   try {
     const collection = await models.Collection.find(collectionId)
     const invitation = await collection.invitations.find(
@@ -33,19 +40,32 @@ module.exports = models => async (req, res) => {
         error: `User is not allowed to modify this invitation.`,
       })
 
-    const params = {
-      baseUrl: helpers.getBaseUrl(req),
-      UserModel: models.User,
-      FragmentModel: models.Fragment,
+    const collectionHelper = new Collection({ collection })
+    const fragment = await models.Fragment.find(last(collection.fragments))
+    const fragmentHelper = new Fragment({ fragment })
+    const parsedFragment = await fragmentHelper.getFragmentData({
+      handlingEditor: collection.handlingEditor,
+    })
+    const baseUrl = services.getBaseUrl(req)
+    const {
+      authorsList: authors,
+      submittingAuthor,
+    } = await collectionHelper.getAuthorData({ UserModel })
+    const emailHelper = new Email({
+      UserModel,
       collection,
-      reviewerName: `${user.firstName} ${user.lastName}`,
-      mailService,
-    }
+      parsedFragment,
+      baseUrl,
+      authors,
+    })
+    const teamHelper = new Team({ TeamModel: models.Team, collectionId })
+    const userHelper = new User({ UserModel })
+
     if (invitation.role === 'handlingEditor')
-      await collectionHelper.updateHandlingEditor(collection, isAccepted)
+      await collectionHelper.updateHandlingEditor({ isAccepted })
     invitation.respondedOn = Date.now()
     invitation.hasAnswer = true
-    const eic = await userHelper.getEditorInChief(models.User)
+    const eic = await userHelper.getEditorInChief()
     const toEmail = eic.email
     if (isAccepted === true) {
       invitation.isAccepted = true
@@ -53,7 +73,7 @@ module.exports = models => async (req, res) => {
         invitation.role === 'reviewer' &&
         collection.status === 'reviewersInvited'
       )
-        await collectionHelper.updateStatus(collection, 'underReview')
+        await collectionHelper.updateStatus({ newStatus: 'underReview' })
 
       await collection.save()
       try {
@@ -62,17 +82,19 @@ module.exports = models => async (req, res) => {
             toEmail,
             user,
             emailType: 'handling-editor-agreed',
-            dashboardUrl: `${req.protocol}://${req.get('host')}`,
+            dashboardUrl: baseUrl,
             meta: {
               collectionId: collection.customId,
             },
           })
         if (invitation.role === 'reviewer')
-          await userHelper.setupReviewerDecisionEmailData({
-            ...params,
+          emailHelper.setupReviewerDecisionEmail({
             agree: true,
             timestamp: invitation.respondedOn,
             user,
+            authorName: `${submittingAuthor.firstName} ${
+              submittingAuthor.lastName
+            }`,
           })
         return res.status(200).json(invitation)
       } catch (e) {
@@ -83,12 +105,11 @@ module.exports = models => async (req, res) => {
       invitation.isAccepted = false
 
       if (invitation.role === 'handlingEditor')
-        await teamHelper.updateHETeam(
+        await teamHelper.updateHETeam({
           collection,
-          invitation.role,
-          models.Team,
+          role: invitation.role,
           user,
-        )
+        })
       if (reason !== undefined) {
         invitation.reason = reason
       }
@@ -106,9 +127,8 @@ module.exports = models => async (req, res) => {
             },
           })
         } else if (invitation.role === 'reviewer') {
-          await collectionHelper.updateReviewerCollectionStatus(collection)
-          await userHelper.setupReviewerDecisionEmailData({
-            ...params,
+          collectionHelper.updateStatusByNumberOfReviewers()
+          emailHelper.setupReviewerDecisionEmail({
             agree: false,
             user,
           })
@@ -118,10 +138,10 @@ module.exports = models => async (req, res) => {
         return res.status(500).json({ error: 'Email could not be sent.' })
       }
     }
-    user = await user.save()
+    user.save()
     return res.status(200).json(invitation)
   } catch (e) {
-    const notFoundError = await helpers.handleNotFoundError(e, 'collection')
+    const notFoundError = await services.handleNotFoundError(e, 'collection')
     return res.status(notFoundError.status).json({
       error: notFoundError.message,
     })
diff --git a/packages/component-invite/src/routes/collectionsInvitations/post.js b/packages/component-invite/src/routes/collectionsInvitations/post.js
index 8f21d2c11d3ce636a6998febd9d71a6da31e5d40..3bafaf954c6276637c2195b93ff0651ab53cdb05 100644
--- a/packages/component-invite/src/routes/collectionsInvitations/post.js
+++ b/packages/component-invite/src/routes/collectionsInvitations/post.js
@@ -1,20 +1,25 @@
-const logger = require('@pubsweet/logger')
-const get = require('lodash/get')
-const helpers = require('../../helpers/helpers')
-const collectionHelper = require('../../helpers/Collection')
-const teamHelper = require('../../helpers/Team')
 const config = require('config')
+const get = require('lodash/get')
+const last = require('lodash/last')
+const logger = require('@pubsweet/logger')
 const mailService = require('pubsweet-component-mail-service')
-const userHelper = require('../../helpers/User')
-const invitationHelper = require('../../helpers/Invitation')
-const authsomeHelper = require('../../helpers/authsome')
+const {
+  Email,
+  services,
+  authsome: authsomeHelper,
+  Fragment,
+  Collection,
+  Team,
+  Invitation,
+  User,
+} = require('pubsweet-component-helper-service')
 
 const configRoles = config.get('roles')
 
 module.exports = models => async (req, res) => {
   const { email, role } = req.body
 
-  if (!helpers.checkForUndefinedParams(email, role)) {
+  if (!services.checkForUndefinedParams(email, role)) {
     res.status(400).json({ error: 'Email and role are required' })
     logger.error('User ID and role are missing')
     return
@@ -25,7 +30,8 @@ module.exports = models => async (req, res) => {
     logger.error(`invitation attempted on invalid role ${role}`)
     return
   }
-  const reqUser = await models.User.find(req.user)
+  const UserModel = models.User
+  const reqUser = await UserModel.find(req.user)
 
   if (email === reqUser.email && !reqUser.admin) {
     logger.error(`${reqUser.email} tried to invite his own email`)
@@ -37,7 +43,7 @@ module.exports = models => async (req, res) => {
   try {
     collection = await models.Collection.find(collectionId)
   } catch (e) {
-    const notFoundError = await helpers.handleNotFoundError(e, 'collection')
+    const notFoundError = await services.handleNotFoundError(e, 'collection')
     return res.status(notFoundError.status).json({
       error: notFoundError.message,
     })
@@ -53,25 +59,36 @@ module.exports = models => async (req, res) => {
     return res.status(403).json({
       error: 'Unauthorized.',
     })
-  const baseUrl = `${req.protocol}://${req.get('host')}`
-  const params = {
-    baseUrl,
-    FragmentModel: models.Fragment,
-    UserModel: models.User,
+
+  const collectionHelper = new Collection({ collection })
+  const fragment = await models.Fragment.find(last(collection.fragments))
+  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 collectionHelper.getAuthorData({ UserModel })
+  const emailHelper = new Email({
+    UserModel,
     collection,
-    mailService,
-    resend: false,
-  }
+    parsedFragment,
+    baseUrl,
+    authors,
+  })
+  const teamHelper = new Team({ TeamModel: models.Team, collectionId })
+  const invitationHelper = new Invitation({ role })
 
   try {
-    const user = await models.User.findByEmail(email)
-
-    await teamHelper.setupManuscriptTeam(models, user, collectionId, role)
-    let invitation = invitationHelper.getInvitation(
-      collection.invitations,
-      user.id,
-      role,
-    )
+    const user = await UserModel.findByEmail(email)
+    await teamHelper.setupManuscriptTeam({ user, role })
+    invitationHelper.userId = user.id
+    let invitation = invitationHelper.getInvitation({
+      invitations: collection.invitations,
+    })
 
     let resend = false
     if (invitation !== undefined) {
@@ -83,32 +100,32 @@ module.exports = models => async (req, res) => {
       await collection.save()
       resend = true
     } else {
-      invitation = await invitationHelper.setupInvitation(
-        user.id,
-        role,
+      invitation = await invitationHelper.setupInvitation({
         collection,
-      )
+      })
     }
 
     try {
       if (role === 'reviewer') {
         if (collection.status === 'heAssigned')
-          await collectionHelper.updateStatus(collection, 'reviewersInvited')
+          await collectionHelper.updateStatus({ newStatus: 'reviewersInvited' })
 
-        await invitationHelper.setupReviewerInvitation({
-          ...params,
+        await emailHelper.setupReviewerInvitationEmail({
           user,
           invitationId: invitation.id,
           timestamp: invitation.invitedOn,
           resend,
+          authorName: `${submittingAuthor.firstName} ${
+            submittingAuthor.lastName
+          }`,
         })
       }
 
       if (role === 'handlingEditor') {
         invitation.invitedOn = Date.now()
         await collection.save()
-        await collectionHelper.addHandlingEditor(collection, user, invitation)
-        await mailService.sendSimpleEmail({
+        await collectionHelper.addHandlingEditor({ user, invitation })
+        mailService.sendSimpleEmail({
           toEmail: user.email,
           user,
           emailType: 'assign-handling-editor',
@@ -121,39 +138,38 @@ module.exports = models => async (req, res) => {
       return res.status(500).json({ error: 'Email could not be sent.' })
     }
   } catch (e) {
+    const userHelper = new User({ UserModel })
     if (role === 'reviewer') {
-      const newUser = await userHelper.setupNewUser(
-        req.body,
-        baseUrl,
-        res,
-        email,
+      const newUser = await userHelper.setupNewUser({
+        url: baseUrl,
         role,
-        models.User,
-        'invite',
-      )
+        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(collection, 'reviewersInvited')
-      await teamHelper.setupManuscriptTeam(models, newUser, collectionId, role)
-      const invitation = await invitationHelper.setupInvitation(
-        newUser.id,
-        role,
+        await collectionHelper.updateStatus({ newStatus: 'reviewersInvited' })
+      await teamHelper.setupManuscriptTeam({ user: newUser, role })
+      invitationHelper.userId = newUser.id
+      const invitation = await invitationHelper.setupInvitation({
         collection,
-      )
+      })
 
-      await invitationHelper.setupReviewerInvitation({
-        ...params,
+      await emailHelper.setupReviewerInvitationEmail({
         user: newUser,
         invitationId: invitation.id,
         timestamp: invitation.invitedOn,
+        authorName: `${submittingAuthor.firstName} ${
+          submittingAuthor.lastName
+        }`,
       })
       return res.status(200).json(invitation)
     }
-    const notFoundError = await helpers.handleNotFoundError(e, 'user')
+    const notFoundError = await services.handleNotFoundError(e, 'user')
     return res.status(notFoundError.status).json({
       error: notFoundError.message,
     })
diff --git a/packages/component-invite/src/tests/collectionsInvitations/get.test.js b/packages/component-invite/src/tests/collectionsInvitations/get.test.js
index 105187c0a834d5c5614b9346318ccdbb715ac8c0..7cf8a64115d3cfd58db7db627638ee37d296f43d 100644
--- a/packages/component-invite/src/tests/collectionsInvitations/get.test.js
+++ b/packages/component-invite/src/tests/collectionsInvitations/get.test.js
@@ -6,6 +6,11 @@ const Model = require('./../helpers/Model')
 const cloneDeep = require('lodash/cloneDeep')
 const requests = require('./../helpers/requests')
 
+jest.mock('pubsweet-component-mail-service', () => ({
+  sendSimpleEmail: jest.fn(),
+  sendNotificationEmail: jest.fn(),
+  sendReviewerInvitationEmail: jest.fn(),
+}))
 const path = '../../routes/collectionsInvitations/get'
 const route = {
   path: '/api/collections/:collectionId/invitations/:invitationId?',
diff --git a/packages/component-invite/src/tests/fixtures/fragments.js b/packages/component-invite/src/tests/fixtures/fragments.js
index 32379359150362143172390427a5113121ad9233..08d0eedf3a531c317cbfb79e58a6d1b019413564 100644
--- a/packages/component-invite/src/tests/fixtures/fragments.js
+++ b/packages/component-invite/src/tests/fixtures/fragments.js
@@ -8,6 +8,7 @@ const fragments = {
       title: chance.sentence(),
       abstract: chance.paragraph(),
     },
+    recommendations: [],
   },
 }
 
diff --git a/packages/component-manuscript-manager/package.json b/packages/component-manuscript-manager/package.json
index 9d40f80a6ae8f8706e7e0fc235e9940a444c8b6b..55ffd9d800fbc02181a01ef7821f74637be8296b 100644
--- a/packages/component-manuscript-manager/package.json
+++ b/packages/component-manuscript-manager/package.json
@@ -25,7 +25,8 @@
   "peerDependencies": {
     "@pubsweet/logger": "^0.0.1",
     "pubsweet-component-mail-service": "0.0.1",
-    "pubsweet-server": "^1.0.1"
+    "pubsweet-server": "^1.0.1",
+    "component-helper-service": "0.0.1"
   },
   "devDependencies": {
     "apidoc": "^0.17.6",
diff --git a/packages/component-manuscript-manager/src/helpers/Collection.js b/packages/component-manuscript-manager/src/helpers/Collection.js
deleted file mode 100644
index 7868b5b9481903203443d0a63c8fc0a716c95d65..0000000000000000000000000000000000000000
--- a/packages/component-manuscript-manager/src/helpers/Collection.js
+++ /dev/null
@@ -1,98 +0,0 @@
-const config = require('config')
-
-const statuses = config.get('statuses')
-
-const updateStatusByRecommendation = async (collection, recommendation) => {
-  let newStatus = 'pendingApproval'
-  if (['minor', 'major'].includes(recommendation))
-    newStatus = 'revisionRequested'
-  collection.status = newStatus
-  collection.visibleStatus = statuses[collection.status].private
-  await collection.save()
-}
-
-const updateFinalStatusByRecommendation = async (
-  collection,
-  recommendation,
-) => {
-  let newStatus
-  switch (recommendation) {
-    case 'reject':
-      newStatus = 'rejected'
-      break
-    case 'publish':
-      newStatus = 'published'
-      break
-    case 'return-to-handling-editor':
-      newStatus = 'reviewCompleted'
-      break
-    default:
-      break
-  }
-  collection.status = newStatus
-  collection.visibleStatus = statuses[collection.status].private
-  await collection.save()
-}
-
-const updateStatus = async (collection, newStatus) => {
-  collection.status = newStatus
-  collection.visibleStatus = statuses[collection.status].private
-  await collection.save()
-}
-
-const getFragmentData = async ({ fragment, handlingEditor }) => {
-  const heRecommendation = fragment.recommendations.find(
-    rec => rec.userId === handlingEditor.id,
-  )
-  let { title, abstract } = fragment.metadata
-  const { type } = fragment.metadata
-  title = title.replace(/<(.|\n)*?>/g, '')
-  abstract = abstract ? abstract.replace(/<(.|\n)*?>/g, '') : ''
-
-  return {
-    title,
-    abstract,
-    type,
-    heRecommendation,
-    recommendations: fragment.recommendations,
-    id: fragment.id,
-  }
-}
-
-const getAuthorData = async ({ authors, UserModel }) => {
-  const submittingAuthorData = authors.find(
-    author => author.isSubmitting === true,
-  )
-  const submittingAuthor = await UserModel.find(submittingAuthorData.userId)
-  const authorsPromises = authors.map(async author => {
-    const user = await UserModel.find(author.userId)
-    return `${user.firstName} ${user.lastName}`
-  })
-  const authorsList = await Promise.all(authorsPromises)
-
-  return {
-    authorsList,
-    submittingAuthor,
-  }
-}
-
-const getReviewerInvitations = (invitations = [], agree = true) =>
-  agree
-    ? invitations.filter(
-        inv =>
-          inv.role === 'reviewer' &&
-          inv.hasAnswer === true &&
-          inv.isAccepted === true,
-      )
-    : invitations.filter(
-        inv => inv.role === 'reviewer' && inv.hasAnswer === false,
-      )
-
-module.exports = {
-  updateStatusByRecommendation,
-  getFragmentData,
-  getAuthorData,
-  getReviewerInvitations,
-  updateStatus,
-  updateFinalStatusByRecommendation,
-}
diff --git a/packages/component-manuscript-manager/src/helpers/authsome.js b/packages/component-manuscript-manager/src/helpers/authsome.js
deleted file mode 100644
index 212cee2a3ea23a424b1f77dde2f7dd4cf888b2a0..0000000000000000000000000000000000000000
--- a/packages/component-manuscript-manager/src/helpers/authsome.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const config = require('config')
-const Authsome = require('authsome')
-
-const mode = require(config.get('authsome.mode'))
-
-const getAuthsome = models =>
-  new Authsome(
-    { ...config.authsome, mode },
-    {
-      // restrict methods passed to mode since these have to be shimmed on client
-      // any changes here should be reflected in the `withAuthsome` component of `pubsweet-client`
-      models: {
-        Collection: {
-          find: id => models.Collection.find(id),
-        },
-        Fragment: {
-          find: id => models.Fragment.find(id),
-        },
-        User: {
-          find: id => models.User.find(id),
-        },
-        Team: {
-          find: id => models.Team.find(id),
-        },
-      },
-    },
-  )
-
-module.exports = { getAuthsome }
diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js
index e629034244dd04c370ad6fde7d3bbef8797f8bd8..fc771b67cbee2448a50bbad958be986c0c7b96d7 100644
--- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js
+++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/patch.js
@@ -1,17 +1,25 @@
-const helpers = require('../../helpers/helpers')
-const authsomeHelper = require('../../helpers/authsome')
-const collectionHelper = require('../../helpers/Collection')
-const Email = require('../../helpers/Email')
+const {
+  Email,
+  services,
+  authsome: authsomeHelper,
+  Fragment,
+  Collection,
+} = require('pubsweet-component-helper-service')
+const logger = require('@pubsweet/logger')
 
 module.exports = models => async (req, res) => {
   const { collectionId, fragmentId, recommendationId } = req.params
   let collection, fragment
   try {
     collection = await models.Collection.find(collectionId)
-    if (!collection.fragments.includes(fragmentId))
+    if (!collection.fragments.includes(fragmentId)) {
+      logger.error(
+        `Collection ${collectionId} does not contain fragment ${fragmentId}`,
+      )
       return res.status(400).json({
         error: `Collection and fragment do not match.`,
       })
+    }
     const authsome = authsomeHelper.getAuthsome(models)
     const target = {
       collection,
@@ -20,10 +28,15 @@ module.exports = models => async (req, res) => {
     const UserModel = models.User
     const user = await UserModel.find(req.user)
     const canPatch = await authsome.can(req.user, 'PATCH', target)
-    if (!canPatch)
+    if (!canPatch) {
+      logger.error(
+        `User ${req.user} is not allowed to access Collection ${collectionId}`,
+      )
       return res.status(403).json({
         error: 'Unauthorized.',
       })
+    }
+
     fragment = await models.Fragment.find(fragmentId)
     const recommendation = fragment.recommendations.find(
       rec => rec.id === recommendationId,
@@ -37,13 +50,13 @@ module.exports = models => async (req, res) => {
     Object.assign(recommendation, req.body)
     recommendation.updatedOn = Date.now()
     if (req.body.submittedOn) {
-      const parsedFragment = await collectionHelper.getFragmentData({
-        fragment,
+      const fragmentHelper = new Fragment({ fragment })
+      const parsedFragment = await fragmentHelper.getFragmentData({
         handlingEditor: collection.handlingEditor,
       })
-      const baseUrl = helpers.getBaseUrl(req)
+      const baseUrl = services.getBaseUrl(req)
+      const collectionHelper = new Collection({ collection })
       const authors = await collectionHelper.getAuthorData({
-        authors: collection.authors,
         UserModel,
       })
       const email = new Email({
@@ -58,12 +71,12 @@ module.exports = models => async (req, res) => {
         reviewerName: `${user.firstName} ${user.lastName}`,
       })
       if (!['pendingApproval', 'revisionRequested'].includes(collection.status))
-        collectionHelper.updateStatus(collection, 'reviewCompleted')
+        collectionHelper.updateStatus({ newStatus: 'reviewCompleted' })
     }
     await fragment.save()
     return res.status(200).json(recommendation)
   } catch (e) {
-    const notFoundError = await helpers.handleNotFoundError(e, 'Item')
+    const notFoundError = await services.handleNotFoundError(e, 'Item')
     return res.status(notFoundError.status).json({
       error: notFoundError.message,
     })
diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js
index c56ec1408a965228941ae92a2bf0dec22fe23729..d5a4a500385cc43e771d01758a78137236320ad7 100644
--- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js
+++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js
@@ -1,13 +1,15 @@
 const uuid = require('uuid')
-
-const helpers = require('../../helpers/helpers')
-const Email = require('../../helpers/Email')
-const authsomeHelper = require('../../helpers/authsome')
-const collectionHelper = require('../../helpers/Collection')
+const {
+  Email,
+  services,
+  authsome: authsomeHelper,
+  Fragment,
+  Collection,
+} = require('pubsweet-component-helper-service')
 
 module.exports = models => async (req, res) => {
   const { recommendation, comments, recommendationType } = req.body
-  if (!helpers.checkForUndefinedParams(recommendationType))
+  if (!services.checkForUndefinedParams(recommendationType))
     return res.status(400).json({ error: 'Recommendation type is required.' })
 
   const reqUser = await models.User.find(req.user)
@@ -24,7 +26,7 @@ module.exports = models => async (req, res) => {
 
     fragment = await models.Fragment.find(fragmentId)
   } catch (e) {
-    const notFoundError = await helpers.handleNotFoundError(e, 'Item')
+    const notFoundError = await services.handleNotFoundError(e, 'Item')
     return res.status(notFoundError.status).json({
       error: notFoundError.message,
     })
@@ -51,15 +53,13 @@ module.exports = models => async (req, res) => {
   newRecommendation.recommendation = recommendation || undefined
   newRecommendation.comments = comments || undefined
   const UserModel = models.User
-  const parsedFragment = await collectionHelper.getFragmentData({
-    fragment,
+  const collectionHelper = new Collection({ collection })
+  const fragmentHelper = new Fragment({ fragment })
+  const parsedFragment = await fragmentHelper.getFragmentData({
     handlingEditor: collection.handlingEditor,
   })
-  const baseUrl = helpers.getBaseUrl(req)
-  const authors = await collectionHelper.getAuthorData({
-    authors: collection.authors,
-    UserModel,
-  })
+  const baseUrl = services.getBaseUrl(req)
+  const authors = await collectionHelper.getAuthorData({ UserModel })
   const email = new Email({
     UserModel,
     collection,
@@ -70,12 +70,11 @@ module.exports = models => async (req, res) => {
 
   if (reqUser.editorInChief || reqUser.admin) {
     if (recommendation === 'return-to-handling-editor')
-      collectionHelper.updateStatus(collection, 'reviewCompleted')
+      collectionHelper.updateStatus({ newStatus: 'reviewCompleted' })
     else {
-      collectionHelper.updateFinalStatusByRecommendation(
-        collection,
+      collectionHelper.updateFinalStatusByRecommendation({
         recommendation,
-      )
+      })
       email.setupAuthorsEmail({
         requestToRevision: false,
         publish: recommendation === 'publish',
@@ -87,10 +86,11 @@ module.exports = models => async (req, res) => {
       email.setupReviewersEmail({
         recommendation,
         isSubmitted: true,
+        agree: true,
       })
     }
   } else if (recommendationType === 'editorRecommendation') {
-    collectionHelper.updateStatusByRecommendation(collection, recommendation)
+    collectionHelper.updateStatusByRecommendation({ recommendation })
     email.setupReviewersEmail({
       recommendation,
       agree: true,
diff --git a/packages/component-user-manager/package.json b/packages/component-user-manager/package.json
index 0d58cca77bc2e2b3c6162fae78e292c2d0a60e08..c65b0553de09100b8c477dfdab49531406d95536 100644
--- a/packages/component-user-manager/package.json
+++ b/packages/component-user-manager/package.json
@@ -25,7 +25,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-helper-service": "0.0.1"
   },
   "devDependencies": {
     "apidoc": "^0.17.6",