diff --git a/packages/component-invite/src/HandleInvitation.js b/packages/component-invite/src/HandleInvitation.js
index d3c5c37336388cfd5779fdd0c5f6a1fc812cdbe0..7ecdd0cafec79703d13c52d1157c766205128286 100644
--- a/packages/component-invite/src/HandleInvitation.js
+++ b/packages/component-invite/src/HandleInvitation.js
@@ -15,6 +15,11 @@ const HandleInvitation = app => {
     authBearer,
     require('./routes/getCollectionUsers')(app.locals.models),
   )
+  app.delete(
+    '/api/collections/:collectionId/users/:userId',
+    authBearer,
+    require('./routes/deleteInvitation')(app.locals.models),
+  )
 }
 
 module.exports = HandleInvitation
diff --git a/packages/component-invite/src/controllers/assignCollectionRole.js b/packages/component-invite/src/controllers/assignCollectionRole.js
index 36a16362ff3ab1e7002ce38375849053bd9802a9..20481676a89a3bf36e74d7ec9f25261bcc5901a3 100644
--- a/packages/component-invite/src/controllers/assignCollectionRole.js
+++ b/packages/component-invite/src/controllers/assignCollectionRole.js
@@ -3,6 +3,7 @@ const config = require('config')
 const helpers = require('../helpers/helpers')
 const teamHelper = require('../helpers/Team')
 const mailService = require('pubsweet-component-mail-service')
+const inviteHelper = require('../helpers/Invitation')
 
 const configRoles = config.get('roles')
 
@@ -79,7 +80,7 @@ module.exports = async (
 
     // getting the updated user from the DB - creating a team also updates the user
     user = await models.User.findByEmail(email)
-    user = await teamHelper.setupInvitation(user, role, collectionId, team.id)
+    user = await inviteHelper.setupInvitation(user, role, collectionId, team.id)
 
     try {
       await mailService.setupAssignEmail(
diff --git a/packages/component-invite/src/helpers/Invitation.js b/packages/component-invite/src/helpers/Invitation.js
new file mode 100644
index 0000000000000000000000000000000000000000..417a3e4c1a28958d2260bf07319504741fbbd34d
--- /dev/null
+++ b/packages/component-invite/src/helpers/Invitation.js
@@ -0,0 +1,41 @@
+const revokeInvitation = async (user, collectionId, type) => {
+  const filteredInvitations = user.invitations.filter(
+    invitation =>
+      invitation.collectionId !== collectionId && invitation.type !== type,
+  )
+
+  user.invitations = filteredInvitations
+  await user.save()
+}
+
+const getInviteData = (invitations, collectionId, role) => {
+  const matchingInvitation = invitations.find(
+    invite => invite.type === role && invite.collectionId === collectionId,
+  )
+  let status = 'pending'
+  if (matchingInvitation.isAccepted) {
+    status = 'accepted'
+  } else if (matchingInvitation.hasAnswer) {
+    status = 'refused'
+  }
+
+  const { timestamp } = matchingInvitation
+  return { timestamp, status }
+}
+
+const setupInvitation = async (user, role, collectionId, teamId) => {
+  const invitation = {
+    type: role,
+    hasAnswer: false,
+    isAccepted: false,
+    collectionId,
+    timestamp: Date.now(),
+    teamId,
+  }
+  user.invitations = user.invitations || []
+  user.invitations.push(invitation)
+  user = await user.save()
+  return user
+}
+
+module.exports = { getInviteData, revokeInvitation, setupInvitation }
diff --git a/packages/component-invite/src/helpers/Team.js b/packages/component-invite/src/helpers/Team.js
index ae6758c967a6ddb379699ffeb1332d81967b55cc..298e0c50834ae3296dda91d19d08de5962df22bc 100644
--- a/packages/component-invite/src/helpers/Team.js
+++ b/packages/component-invite/src/helpers/Team.js
@@ -113,26 +113,13 @@ const getMatchingTeams = (teams, TeamModel, collectionId, role) =>
     })
     .filter(Boolean)
 
-const setupInvitation = async (user, role, collectionId, teamId) => {
-  const invitation = {
-    type: role,
-    hasAnswer: false,
-    isAccepted: false,
-    collectionId,
-    timestamp: Date.now(),
-    teamId,
-  }
-  user.invitations = user.invitations || []
-  user.invitations.push(invitation)
-  user = await user.save()
-  return user
-}
-
 const removeTeamMember = async (teamId, userId, TeamModel) => {
-  const team = await TeamModel.find(teamId)
+  let team = await TeamModel.find(teamId)
   const members = team.members.filter(member => member !== userId)
   team.members = members
-  await TeamModel.updateProperties(team)
+
+  team = await team.updateProperties(team)
+
   await team.save()
 }
 
@@ -151,21 +138,6 @@ const getTeamMembersByCollection = async (collectionId, role, TeamModel) => {
   return members
 }
 
-const getInviteData = (invitations, collectionId, role) => {
-  const matchingInvitation = invitations.find(
-    invite => invite.type === role && invite.collectionId === collectionId,
-  )
-  let status = 'pending'
-  if (matchingInvitation.isAccepted) {
-    status = 'accepted'
-  } else if (matchingInvitation.hasAnswer) {
-    status = 'refused'
-  }
-
-  const { timestamp } = matchingInvitation
-  return { timestamp, status }
-}
-
 const getTeamByGroupAndCollection = async (collectionId, role, TeamModel) => {
   const teams = await TeamModel.all()
   return teams.find(
@@ -181,9 +153,7 @@ module.exports = {
   setupEiCTeams,
   setupManuscriptTeam,
   getMatchingTeams,
-  setupInvitation,
   removeTeamMember,
   getTeamMembersByCollection,
-  getInviteData,
   getTeamByGroupAndCollection,
 }
diff --git a/packages/component-invite/src/helpers/helpers.js b/packages/component-invite/src/helpers/helpers.js
index 2ec5f5f9f7d8ce52f0bba0f12417d8a3f9e09128..3be4674565565f5f29588f90cfb27f3f6172aa28 100644
--- a/packages/component-invite/src/helpers/helpers.js
+++ b/packages/component-invite/src/helpers/helpers.js
@@ -84,10 +84,12 @@ const createNewUser = async (
   UserModel,
   role,
 ) => {
+  const username = uuid.v4().slice(0, 8)
+  const password = uuid.v4()
   const userBody = {
-    username: uuid.v4().slice(0, 8),
+    username,
     email,
-    password: uuid.v4(),
+    password,
     passwordResetToken: crypto.randomBytes(32).toString('hex'),
     isConfirmed: false,
     firstName,
@@ -98,7 +100,9 @@ const createNewUser = async (
     admin: role === 'admin',
     handlingEditor: role === 'handlingEditor',
   }
+
   let newUser = new UserModel(userBody)
+
   try {
     newUser = await newUser.save()
     return newUser
diff --git a/packages/component-invite/src/routes/deleteInvitation.js b/packages/component-invite/src/routes/deleteInvitation.js
new file mode 100644
index 0000000000000000000000000000000000000000..d8c77d72d2d0717ae32a44291a97f2e33aec5ca2
--- /dev/null
+++ b/packages/component-invite/src/routes/deleteInvitation.js
@@ -0,0 +1,63 @@
+const helpers = require('../helpers/helpers')
+const teamHelper = require('../helpers/Team')
+const config = require('config')
+const inviteHelper = require('../helpers/Invitation')
+const mailService = require('pubsweet-component-mail-service')
+const logger = require('@pubsweet/logger')
+
+const configRoles = config.get('roles')
+module.exports = models => async (req, res) => {
+  const { role } = req.query
+  if (!helpers.checkForUndefinedParams(role)) {
+    res.status(400).json({ error: 'Role is required' })
+    return
+  }
+
+  if (!configRoles.collection.includes(role)) {
+    res.status(400).json({ error: `Role ${role} is invalid` })
+    return
+  }
+
+  const reqUser = await models.User.find(req.user)
+  if (!reqUser.editorInChief) {
+    res.status(400).json({ error: 'The request user must be Editor in Chief' })
+    return
+  }
+
+  const { collectionId, userId } = req.params
+  try {
+    await models.Collection.find(collectionId)
+    let user = await models.User.find(userId)
+    const team = await teamHelper.getTeamByGroupAndCollection(
+      collectionId,
+      role,
+      models.Team,
+    )
+
+    if (team === undefined) {
+      res.status(400).json({
+        error: `The requested collection does not have a ${role} Team`,
+      })
+      return
+    }
+    await inviteHelper.revokeInvitation(user, collectionId, role)
+    user = await models.User.find(userId)
+    await teamHelper.removeTeamMember(team.id, userId, models.Team)
+    try {
+      await mailService.setupRevokeInvitationEmail(
+        user.email,
+        'revoke-handling-editor',
+      )
+
+      return res.status(204).json()
+    } catch (e) {
+      logger.error(e.message)
+      return res.status(500).json({ error: 'Email could not be sent.' })
+    }
+  } catch (e) {
+    const notFoundError = await helpers.handleNotFoundError(e, 'item')
+    return res.status(notFoundError.status).json({
+      error: notFoundError.message,
+    })
+  }
+}
diff --git a/packages/component-invite/src/routes/getCollectionUsers.js b/packages/component-invite/src/routes/getCollectionUsers.js
index 16903452e5d2a81805b118dd5f3886dbe549d987..94570732b4731e170f6e9d6a076258631d1e2de7 100644
--- a/packages/component-invite/src/routes/getCollectionUsers.js
+++ b/packages/component-invite/src/routes/getCollectionUsers.js
@@ -1,6 +1,7 @@
 const helpers = require('../helpers/helpers')
 const teamHelper = require('../helpers/Team')
 const config = require('config')
+const inviteHelper = require('../helpers/Invitation')
 
 const configRoles = config.get('roles')
 module.exports = models => async (req, res) => {
@@ -39,7 +40,7 @@ module.exports = models => async (req, res) => {
 
     const membersData = members.map(async member => {
       const user = await models.User.find(member)
-      const { timestamp, status } = teamHelper.getInviteData(
+      const { timestamp, status } = inviteHelper.getInviteData(
         user.invitations,
         collectionId,
         role,
diff --git a/packages/component-invite/src/tests/deleteInvitation.test.js b/packages/component-invite/src/tests/deleteInvitation.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..30e994b12abf5365195570f6d48892815709e6f7
--- /dev/null
+++ b/packages/component-invite/src/tests/deleteInvitation.test.js
@@ -0,0 +1,107 @@
+process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
+process.env.SUPPRESS_NO_CONFIG_WARNING = true
+
+const httpMocks = require('node-mocks-http')
+const fixtures = require('./fixtures/fixtures')
+const Model = require('./helpers/Model')
+
+jest.mock('pubsweet-component-mail-service', () => ({
+  setupRevokeInvitationEmail: jest.fn(),
+}))
+const { standardCollection, noTeamCollection } = fixtures.collections
+const { editorInChief, admin, handlingEditor } = fixtures.users
+const { heTeam } = fixtures.teams
+const query = {
+  role: 'handlingEditor',
+}
+const deleteInvitationPath = '../routes/deleteInvitation'
+const models = Model.build()
+
+describe('Delete Invitation route handler', () => {
+  it('should return success when the role is correct, the collection exists and the request user is editorInChief ', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    req.params.collectionId = standardCollection.id
+    req.params.userId = handlingEditor.id
+    req.user = editorInChief.id
+    const res = httpMocks.createResponse()
+    const initialSize = handlingEditor.invitations.length
+    await require(deleteInvitationPath)(models)(req, res)
+    expect(res.statusCode).toBe(204)
+    expect(heTeam.members).not.toContain(handlingEditor.id)
+    expect(handlingEditor.invitations).toHaveLength(initialSize - 1)
+  })
+  it('should return an error when the role parameter is missing', async () => {
+    delete query.role
+    const req = httpMocks.createRequest()
+    req.query = query
+    req.user = editorInChief.id
+    const res = httpMocks.createResponse()
+    await require(deleteInvitationPath)(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('Role is required')
+    query.role = 'handlingEditor'
+  })
+  it('should return an error when the collection does not exist', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    req.params.collectionId = 'invalid-id'
+    req.user = editorInChief.id
+    const res = httpMocks.createResponse()
+    await require(deleteInvitationPath)(models)(req, res)
+    expect(res.statusCode).toBe(404)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('item not found')
+  })
+  it('should return an error when the user does not exist', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    req.params.collectionId = standardCollection.id
+    req.params.userId = 'invalid-id'
+    req.user = editorInChief.id
+    const res = httpMocks.createResponse()
+    await require(deleteInvitationPath)(models)(req, res)
+    expect(res.statusCode).toBe(404)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('item not found')
+  })
+  it('should return an error when the role is invalid', async () => {
+    query.role = 'invalidRole'
+    const req = httpMocks.createRequest()
+    req.query = query
+    req.params.collectionId = standardCollection.id
+    req.user = editorInChief.id
+    const res = httpMocks.createResponse()
+    await require(deleteInvitationPath)(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual(`Role ${query.role} is invalid`)
+    query.role = 'handlingEditor'
+  })
+  it('should return an error when the request user is not editorInChief', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    req.params.collectionId = standardCollection.id
+    req.user = admin.id
+    const res = httpMocks.createResponse()
+    await require(deleteInvitationPath)(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('The request user must be Editor in Chief')
+  })
+  it('should return an error when the collection does not have a the requested role team', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    req.params.collectionId = noTeamCollection.id
+    req.params.userId = handlingEditor.id
+    req.user = editorInChief.id
+    const res = httpMocks.createResponse()
+    await require(deleteInvitationPath)(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual(
+      `The requested collection does not have a ${query.role} Team`,
+    )
+  })
+})
diff --git a/packages/component-invite/src/tests/fixtures/teamIDs.js b/packages/component-invite/src/tests/fixtures/teamIDs.js
index f8e1c9f2285384a39ccb39de082abf773c7cfb02..74f1842bb26490263c8f58d52cee2b662c72b6cd 100644
--- a/packages/component-invite/src/tests/fixtures/teamIDs.js
+++ b/packages/component-invite/src/tests/fixtures/teamIDs.js
@@ -1,10 +1,10 @@
 const Chance = require('chance')
 
 const chance = new Chance()
+const heID = chance.guid()
+const revID = chance.guid()
 
-const teamIDs = {
-  heTeam: chance.guid(),
-  reviewerTeam: chance.guid(),
+module.exports = {
+  heTeamID: heID,
+  reviewerTeamID: revID,
 }
-
-module.exports = { teamIDs }
diff --git a/packages/component-invite/src/tests/fixtures/teams.js b/packages/component-invite/src/tests/fixtures/teams.js
index 84de7cd6b6284772d1516308692709f0031a8f6d..1cfc277b62badf8a17111524e4e0e62622628795 100644
--- a/packages/component-invite/src/tests/fixtures/teams.js
+++ b/packages/component-invite/src/tests/fixtures/teams.js
@@ -1,6 +1,6 @@
 const users = require('./users')
 const collections = require('./collections')
-const { heTeam, reviewerTeam } = require('./teamIDs')
+const { heTeamID, reviewerTeamID } = require('./teamIDs')
 
 const { standardCollection } = collections
 const { editorInChief, handlingEditor, reviewer, invitedHandlingEditor } = users
@@ -34,7 +34,7 @@ const teams = {
     members: [handlingEditor.id, invitedHandlingEditor.id],
     save: jest.fn(() => teams.heTeam),
     updateProperties: jest.fn(() => teams.heTeam),
-    id: heTeam,
+    id: heTeamID,
   },
   reviewerTeam: {
     teamType: {
@@ -50,7 +50,7 @@ const teams = {
     members: [reviewer.id],
     save: jest.fn(() => teams.reviewerTeam),
     updateProperties: jest.fn(() => teams.reviewerTeam),
-    id: reviewerTeam,
+    id: reviewerTeamID,
   },
 }
 module.exports = teams
diff --git a/packages/component-invite/src/tests/fixtures/users.js b/packages/component-invite/src/tests/fixtures/users.js
index a04b7ff4c5e65b0b444f1a085b2c6fcc1254ed4e..53e00485ec4413381309488acb126c2ccb4b2dfe 100644
--- a/packages/component-invite/src/tests/fixtures/users.js
+++ b/packages/component-invite/src/tests/fixtures/users.js
@@ -1,5 +1,5 @@
 const { standardCollection } = require('./collections')
-const { heTeam, reviewerTeam } = require('./teamIDs')
+const { heTeamID, reviewerTeamID } = require('./teamIDs')
 
 const users = {
   admin: {
@@ -42,10 +42,10 @@ const users = {
         isAccepted: false,
         collectionId: standardCollection.id,
         timestamp: Date.now(),
-        teamId: heTeam,
+        teamId: heTeamID,
       },
     ],
-    teams: [heTeam],
+    teams: [heTeamID],
     save: jest.fn(() => users.handlingEditor),
     editorInChief: false,
   },
@@ -78,7 +78,7 @@ const users = {
     title: 'Ms',
     save: jest.fn(() => users.reviewer),
     isConfirmed: false,
-    teams: [reviewerTeam],
+    teams: [reviewerTeamID],
     invitations: [
       {
         type: 'reviewer',
@@ -86,7 +86,7 @@ const users = {
         isAccepted: false,
         collectionId: '123',
         timestamp: Date.now(),
-        teamId: reviewerTeam,
+        teamId: reviewerTeamID,
       },
     ],
   },
@@ -106,10 +106,10 @@ const users = {
         isAccepted: false,
         collectionId: standardCollection.id,
         timestamp: Date.now(),
-        teamId: heTeam,
+        teamId: heTeamID,
       },
     ],
-    teams: [heTeam],
+    teams: [heTeamID],
     save: jest.fn(() => users.handlingEditor),
     editorInChief: false,
   },
diff --git a/packages/component-mail-service/package.json b/packages/component-mail-service/package.json
index cb79ab3a4626231243fd6149417cd338d179da7a..5a8328e59fbbeab1b75e18cabfa51879627adc0d 100644
--- a/packages/component-mail-service/package.json
+++ b/packages/component-mail-service/package.json
@@ -4,7 +4,9 @@
   "description": "mail service component for pubsweet",
   "license": "MIT",
   "author": "Collaborative Knowledge Foundation",
-  "files": ["src"],
+  "files": [
+    "src"
+  ],
   "main": "index.js",
   "repository": {
     "type": "git",
@@ -12,13 +14,13 @@
     "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",
-    "@pubsweet/component-send-email": "^0.1.0"
+    "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
index df5e75ba50eb06507cae43596d20af68a18e8f76..11d1f29f20684c90abf2c8e8f0df3e2f3baeb2fc 100644
--- a/packages/component-mail-service/src/Mail.js
+++ b/packages/component-mail-service/src/Mail.js
@@ -51,6 +51,28 @@ module.exports = {
         break
     }
 
+    const { htmlBody, textBody } = getEmailBody(emailType, replacements)
+    const mailData = {
+      from: config.get('mailer.from'),
+      to: email,
+      subject,
+      text: textBody,
+      html: htmlBody,
+    }
+    return Email.send(mailData)
+  },
+  setupRevokeInvitationEmail: async (email, emailType) => {
+    let subject
+    const replacements = {}
+    switch (emailType) {
+      case 'revoke-handling-editor':
+        subject = 'Invitation has been Cancelled'
+        break
+      default:
+        subject = 'Welcome to Hindawi!'
+        break
+    }
+
     const { htmlBody, textBody } = getEmailBody(emailType, replacements)
     const mailData = {
       from: config.get('mailer.from'),
diff --git a/packages/component-mail-service/src/templates/revoke-handling-editor.html b/packages/component-mail-service/src/templates/revoke-handling-editor.html
new file mode 100644
index 0000000000000000000000000000000000000000..1570b897d6c628c28036b391150ac9717ee1c4be
--- /dev/null
+++ b/packages/component-mail-service/src/templates/revoke-handling-editor.html
@@ -0,0 +1,211 @@
+
+<!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>revoke handling editor</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>
+
+                              <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;">Your Handling Editor invitation to a manuscript has been revoked.</h1>
+
+                                    <div style="text-align: center;">&nbsp;</div>
+
+                                    <div style="text-align: center;">&nbsp;</div>
+
+                                  </td>
+                                </tr>
+                              </table>
+                              <div data-role="module-unsubscribe" class="module unsubscribe-css__unsubscribe___2CDlR" role="module"
+                                data-type="unsubscribe" style="color:#444444;font-size:12px;line-height:20px;padding:16px 16px 16px 16px;text-align:center">
+                                <div class="Unsubscribe--addressLine">
+                                  <p class="Unsubscribe--senderName" style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px">Hindawi Publishing Corporation</p>
+                                  <p style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px">
+                                    <span class="Unsubscribe--senderAddress">315 Madison Ave, Third Floor, Suite 3070</span>,
+                                    <span class="Unsubscribe--senderCity">NEW YORK</span>,
+                                    <span class="Unsubscribe--senderState">NY</span>
+                                    <span class="Unsubscribe--senderZip">10017</span>
+                                  </p>
+                                </div>
+                                <p style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px">
+                                  <a class="Unsubscribe--unsubscribeLink" href="[Unsubscribe]">Unsubscribe</a>
+                                </p>
+                              </div>
+                            </td>
+                          </tr>
+                        </table>
+                        <!--[if mso]>
+                          </td></tr></table>
+                          </center>
+                          <![endif]-->
+                      </td>
+                    </tr>
+                  </table>
+                </td>
+              </tr>
+            </table>
+          </td>
+        </tr>
+      </table>
+    </div>
+  </center>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/revoke-handling-editor.txt b/packages/component-mail-service/src/templates/revoke-handling-editor.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e871a6f4c2222bc614910755c4514cfa2f2b3249
--- /dev/null
+++ b/packages/component-mail-service/src/templates/revoke-handling-editor.txt
@@ -0,0 +1,4 @@
+Your Handling Editor invitation to a manuscript has been revoked.
+Hindawi Publishing Corporation
+315 Madison Ave, Third Floor, Suite 307
+New York, NY 10017
\ No newline at end of file
diff --git a/packages/component-send-email/.gitignore b/packages/component-send-email/.gitignore
deleted file mode 100644
index 3614a810088d89d9ccaa28d82401545634874a18..0000000000000000000000000000000000000000
--- a/packages/component-send-email/.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-send-email/README.md b/packages/component-send-email/README.md
deleted file mode 100644
index 6107c057207697467a41159bcc3f0eee270ae181..0000000000000000000000000000000000000000
--- a/packages/component-send-email/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# AWS SES
-
-In order to use `component-aws-ses` you first need to have a `.env` file containing AWS data in the root folder of the starting point of your application.
-
-The `.env` file contain the following data:
-```bash
-AWS_SES_SECRET_KEY = <secretKey>
-AWS_SES_ACCESS_KEY = <accessKey>
-EMAIL_SENDER = verified_ses_sender@domain.com
-AWS_SES_REGION = region-name
-```
-
-Then, as soon as possible in your app you should add the `dotenv` package:
-```js
-require('dotenv').config()
-```
-
-# `component-aws-ses` API
-A list of endpoints that help you upload, download and delete S3 files.
-
-## Send an email [POST]
-#### Request
-`POST /api/email`
-#### Request body
-
-All parameters are `required`
-```json
-{
-	"email": "to_email@domain.com",
-	"subject": "Example subject",
-	"textBody": "this is an email",
-	"htmlBody": "<p><b>This</b> is an <i>email</i>"
-}
-```
-#### Response
-```json
-HTTP/1.1 204
-```
-
-
-
-
diff --git a/packages/component-send-email/index.js b/packages/component-send-email/index.js
deleted file mode 100644
index 638be56beb2320e02d60ee4601a570c689d7ba95..0000000000000000000000000000000000000000
--- a/packages/component-send-email/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-require('dotenv').config()
-
-module.exports = require('./src/SendEmail')
diff --git a/packages/component-send-email/package.json b/packages/component-send-email/package.json
deleted file mode 100644
index 42a1c5d72bf92faf735616688e10c221a5b0cf05..0000000000000000000000000000000000000000
--- a/packages/component-send-email/package.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "name": "@pubsweet/component-send-email",
-  "version": "0.2.0",
-  "description": "xpub aws ses configured for faraday",
-  "license": "MIT",
-  "files": [
-    "src"
-  ],
-  "repository": {
-    "type": "git",
-    "url": "https://gitlab.coko.foundation/xpub/xpub"
-  },
-  "dependencies": {
-    "aws-sdk": "^2.185.0",
-    "nodemailer": "^4.4.2"
-  },
-  "peerDependencies": {
-    "@pubsweet/logger": "^0.0.1"
-  }
-}
diff --git a/packages/component-send-email/src/SendEmail.js b/packages/component-send-email/src/SendEmail.js
deleted file mode 100644
index fa16450219d8b3b8cd29d8987f92afd3859f7b42..0000000000000000000000000000000000000000
--- a/packages/component-send-email/src/SendEmail.js
+++ /dev/null
@@ -1,10 +0,0 @@
-const nodemailer = require('nodemailer')
-const config = require('config')
-
-const mailerConfig = require(config.mailer.path)
-module.exports = {
-  send: mailData => {
-    const transporter = nodemailer.createTransport(mailerConfig.transport)
-    return transporter.sendMail(mailData)
-  },
-}
diff --git a/yarn.lock b/yarn.lock
index 8ffb522b4d849f6b522ee7d55e86bb0a682a39e8..5f06aabe0960b5f12163e9e80553996086a8d8cf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -90,6 +90,13 @@
     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"
+  dependencies:
+    aws-sdk "^2.185.0"
+    nodemailer "^4.4.2"
+
 "@pubsweet/db-manager@^1.0.3":
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/@pubsweet/db-manager/-/db-manager-1.0.3.tgz#e8784c87eee958ed17daa12f54625774bd2fd277"