diff --git a/packages/component-manuscript/src/components/ManuscriptLayout.js b/packages/component-manuscript/src/components/ManuscriptLayout.js
index 527861a2f908403d9e0aa00fc7c239df2e3f28d6..c5ffbe1a4ef289f530893ca9ada6704c48e4fb40 100644
--- a/packages/component-manuscript/src/components/ManuscriptLayout.js
+++ b/packages/component-manuscript/src/components/ManuscriptLayout.js
@@ -29,8 +29,6 @@ const cannotViewReviewersDetails = ['revisionRequested', 'pendingApproval']
 
 const ManuscriptLayout = ({
   history,
-  assignHE,
-  revokeHE,
   currentUser,
   getSignedUrl,
   editorInChief,
@@ -44,7 +42,6 @@ const ManuscriptLayout = ({
   fetchingError,
   formValues,
   heExpanded,
-  onHEResponse,
   toggleAssignHE,
   toggleHEResponse,
   heResponseExpanded,
@@ -66,6 +63,7 @@ const ManuscriptLayout = ({
   submitRevision,
   inviteReviewer,
   recommendationHandle,
+  inviteHandlingEditor,
 }) => (
   <Root pb={30}>
     {!isEmpty(collection) && !isEmpty(fragment) ? (
@@ -86,8 +84,8 @@ const ManuscriptLayout = ({
           inviteHE={toggleAssignHE}
           isFetching={isFetching.editorsFetching}
           journal={journal}
-          resendInvitation={assignHE}
-          revokeInvitation={revokeHE}
+          resendInvitation={inviteHandlingEditor.assignHE}
+          revokeInvitation={inviteHandlingEditor.revokeHE}
         />
 
         <ManuscriptMetadata
@@ -139,7 +137,7 @@ const ManuscriptLayout = ({
             expanded={heResponseExpanded}
             formValues={formValues.responseToInvitation}
             label="Do you agree to be the handling editor for this manuscript?"
-            onResponse={onHEResponse}
+            onResponse={inviteHandlingEditor.onHEResponse}
             title="Respond to Editorial Invitation"
             toggle={toggleHEResponse}
           />
@@ -156,7 +154,7 @@ const ManuscriptLayout = ({
         )}
 
         <ManuscriptAssignHE
-          assignHE={assignHE}
+          assignHE={inviteHandlingEditor.assignHE}
           currentUser={currentUser}
           expanded={heExpanded}
           handlingEditors={handlingEditors}
diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js
index 2dc4481e221450dbac99107b24d6bf1ff42a34df..5314980bc401ad0016fe46fdb3275716f7e878fb 100644
--- a/packages/component-manuscript/src/components/ManuscriptPage.js
+++ b/packages/component-manuscript/src/components/ManuscriptPage.js
@@ -65,6 +65,7 @@ import ManuscriptLayout from './ManuscriptLayout'
 import withInviteReviewer from '../inviteReviewer/withInviteReviewer'
 import withSubmitRevision from '../submitRevision/withSubmitRevision'
 import withHandleRecommendation from '../handleRecommendation/withHandleRecommendation'
+import withInviteHandlingEditor from '../inviteHandlingEditor/withInviteHandlingEditor'
 import {
   parseSearchParams,
   redirectToError,
@@ -74,10 +75,7 @@ import {
   canAssignHE,
   selectFetching,
   getHandlingEditors,
-  assignHandlingEditor,
-  revokeHandlingEditor,
   selectHandlingEditors,
-  handlingEditorDecision,
 } from '../redux/editors'
 
 export default compose(
@@ -128,7 +126,6 @@ export default compose(
     {
       changeForm,
       clearCustomError,
-      revokeHandlingEditor,
       getUsers: actions.getUsers,
       getFragment: actions.getFragment,
       getCollection: actions.getCollection,
@@ -255,62 +252,6 @@ export default compose(
         setEiC(`${firstName} ${lastName}`)
       }
     },
-    assignHE: ({
-      setFetching,
-      fetchUpdatedCollection,
-      collection: { id: collectionId },
-    }) => (email, modalProps) =>
-      assignHandlingEditor({
-        email,
-        collectionId,
-      })
-        .then(() => {
-          fetchUpdatedCollection()
-          modalProps.hideModal()
-        })
-        .catch(handleError(modalProps.setModalError)),
-    revokeHE: ({
-      getCollection,
-      revokeHandlingEditor,
-      collection: { id: collectionId },
-    }) => (invitationId, modalProps) =>
-      revokeHandlingEditor({
-        invitationId,
-        collectionId,
-      })
-        .then(() => {
-          getCollection({ id: collectionId })
-          modalProps.hideModal()
-        })
-        .catch(handleError(modalProps.setModalError)),
-    onHEResponse: ({
-      history,
-      collection,
-      pendingHEInvitation,
-      fetchUpdatedCollection,
-    }) => (values, { hideModal, setModalError, setFetching }) => {
-      const isAccepted = get(values, 'decision', 'decline') === 'accept'
-      setFetching(true)
-      return handlingEditorDecision({
-        isAccepted,
-        collectionId: collection.id,
-        reason: get(values, 'reason', ''),
-        invitationId: pendingHEInvitation.id,
-      })
-        .then(() => {
-          setFetching(false)
-          hideModal()
-          if (isAccepted) {
-            fetchUpdatedCollection()
-          } else {
-            history.replace('/')
-          }
-        })
-        .catch(err => {
-          setFetching(false)
-          handleError(setModalError)(err)
-        })
-    },
   }),
   fromRenderProps(RemoteOpener, ({ toggle, expanded }) => ({
     toggleAssignHE: toggle,
@@ -342,6 +283,7 @@ export default compose(
       get(currentUser, 'isReviewer', false) &&
       isUndefined(submittedOwnRecommendation),
   })),
+  withInviteHandlingEditor,
   withInviteReviewer,
   withSubmitRevision,
   withHandleRecommendation,
diff --git a/packages/component-manuscript/src/inviteHandlingEditor/inviteHE.api.js b/packages/component-manuscript/src/inviteHandlingEditor/inviteHE.api.js
new file mode 100644
index 0000000000000000000000000000000000000000..ff05375a89ce5afde5544208908a024d131445f9
--- /dev/null
+++ b/packages/component-manuscript/src/inviteHandlingEditor/inviteHE.api.js
@@ -0,0 +1,31 @@
+import { create, update, remove } from 'pubsweet-client/src/helpers/api'
+
+export const handlingEditorDecision = ({
+  reason,
+  isAccepted,
+  collectionId,
+  invitationId,
+}) =>
+  update(`/collections/${collectionId}/invitations/${invitationId}`, {
+    isAccepted,
+    reason,
+  })
+
+export const assignHandlingEditor = ({ email, collectionId }) =>
+  create(`/collections/${collectionId}/invitations`, {
+    email,
+    role: 'handlingEditor',
+  }).then(
+    res => res,
+    err => {
+      throw err
+    },
+  )
+
+export const revokeHandlingEditor = ({ invitationId, collectionId }) =>
+  remove(`/collections/${collectionId}/invitations/${invitationId}`).then(
+    res => res,
+    err => {
+      throw err
+    },
+  )
diff --git a/packages/component-manuscript/src/inviteHandlingEditor/withInviteHandlingEditor.js b/packages/component-manuscript/src/inviteHandlingEditor/withInviteHandlingEditor.js
new file mode 100644
index 0000000000000000000000000000000000000000..693de566d9cc3a8bea7c1c78d3f3e4797cfc6c9c
--- /dev/null
+++ b/packages/component-manuscript/src/inviteHandlingEditor/withInviteHandlingEditor.js
@@ -0,0 +1,75 @@
+import { compose, withHandlers, withProps } from 'recompose'
+import { get, pick } from 'lodash'
+import { withRouter } from 'react-router-dom'
+import { handleError, withFetching } from 'pubsweet-component-faraday-ui'
+import {
+  handlingEditorDecision,
+  assignHandlingEditor,
+  revokeHandlingEditor,
+} from './inviteHE.api'
+
+export default compose(
+  withFetching,
+  withRouter,
+  withHandlers({
+    assignHE: ({
+      fetchUpdatedCollection,
+      collection: { id: collectionId },
+    }) => (email, modalProps) =>
+      assignHandlingEditor({
+        email,
+        collectionId,
+      })
+        .then(() => {
+          fetchUpdatedCollection()
+          modalProps.hideModal()
+        })
+        .catch(handleError(modalProps.setModalError)),
+    revokeHE: ({ getCollection, collection: { id: collectionId } }) => (
+      invitationId,
+      modalProps,
+    ) =>
+      revokeHandlingEditor({
+        invitationId,
+        collectionId,
+      })
+        .then(() => {
+          getCollection({ id: collectionId })
+          modalProps.hideModal()
+        })
+        .catch(handleError(modalProps.setModalError)),
+    onHEResponse: ({
+      history,
+      collection,
+      pendingHEInvitation,
+      fetchUpdatedCollection,
+    }) => (values, { hideModal, setModalError, setFetching }) => {
+      const isAccepted = get(values, 'decision', 'decline') === 'accept'
+      setFetching(true)
+      return handlingEditorDecision({
+        isAccepted,
+        collectionId: collection.id,
+        reason: get(values, 'reason', ''),
+        invitationId: pendingHEInvitation.id,
+      })
+        .then(() => {
+          setFetching(false)
+          hideModal()
+          if (isAccepted) {
+            fetchUpdatedCollection()
+          } else {
+            history.replace('/')
+          }
+        })
+        .catch(err => {
+          setFetching(false)
+          handleError(setModalError)(err)
+        })
+    },
+  }),
+  withProps(props => ({
+    inviteHandlingEditor: {
+      ...pick(props, ['assignHE', 'revokeHE', 'onHEResponse']),
+    },
+  })),
+)