diff --git a/.gitlab-ci.aperture.yml b/.gitlab-ci.aperture.yml
index c168b2d7d7d8a2b2f948f3fed0371b4247417895..0a0dee3462cc4641c8d92e88388092b1221c0137 100644
--- a/.gitlab-ci.aperture.yml
+++ b/.gitlab-ci.aperture.yml
@@ -54,6 +54,7 @@ test-chrome:
     - postgres
   script:
     - cd ${HOME}
+    - apt-get update
     - apt-get -y install postgresql-client
     # this is needed for pgboss initial setup
     - psql -h postgres -U kotahidev -d kotahidev -c "create extension pgcrypto;"
diff --git a/app/components/component-formbuilder/src/components/builderComponents/RadioBox.js b/app/components/component-formbuilder/src/components/builderComponents/RadioBox.js
index f0302c923999488e0bff14579e63e136db2ed916..cd1e92ece70b72b6194266ec6cc3ebfa4c365096 100644
--- a/app/components/component-formbuilder/src/components/builderComponents/RadioBox.js
+++ b/app/components/component-formbuilder/src/components/builderComponents/RadioBox.js
@@ -1,5 +1,10 @@
 import React from 'react'
-import { RadioGroup } from '@pubsweet/ui'
+import styled from 'styled-components'
+import { RadioGroup as UnstableRadioGroup } from '@pubsweet/ui'
+
+const RadioGroup = styled(UnstableRadioGroup)`
+  position: relative
+`
 
 const RadioboxFieldBuilder = input => <RadioGroup {...input} />
 export default RadioboxFieldBuilder
diff --git a/app/components/component-formbuilder/src/components/config/Elements.js b/app/components/component-formbuilder/src/components/config/Elements.js
index c46a8e7f2723809ba3fa773e1eb45b7725783dc3..1729f0c0bafcd83aea3c3380908c2b0c1a809188 100644
--- a/app/components/component-formbuilder/src/components/config/Elements.js
+++ b/app/components/component-formbuilder/src/components/config/Elements.js
@@ -147,6 +147,23 @@ const elements = {
         ],
       },
     },
+    DoiValidation: {
+      component: 'RadioBox',
+      props: {
+        inline: true,
+        options: [
+          {
+            value: 'true',
+            label: 'Yes',
+          },
+          {
+            value: 'false',
+            label: 'No',
+          },
+        ],
+        label: 'DOI Validation',
+      },
+    },
   },
   CheckboxGroup: {
     id: textfield,
diff --git a/app/components/component-submit/src/components/FormTemplate.js b/app/components/component-submit/src/components/FormTemplate.js
index 7a80fbf347d9bf5df18c44ba8d19e2c9149ce839..84e52cae8155ccbc98db368e85be9a3f6b8f3a35 100644
--- a/app/components/component-submit/src/components/FormTemplate.js
+++ b/app/components/component-submit/src/components/FormTemplate.js
@@ -19,6 +19,8 @@ import LinksInput from './LinksInput'
 import ValidatedFieldFormik from './ValidatedField'
 import Confirm from './Confirm'
 import { articleStatuses } from '../../../../globals'
+import { VALIDATE_DOI } from '../../../../queries/index'
+import { useApolloClient } from '@apollo/client'
 
 const Intro = styled.div`
   font-style: italic;
@@ -110,7 +112,7 @@ const createMarkup = encodedHtml => ({
   __html: unescape(encodedHtml),
 })
 
-const composeValidate = (vld = [], valueField = {}) => value => {
+const composeValidate = (vld = [], valueField = {}, fieldName, client) => value => {
   const validator = vld || []
 
   if (validator.length === 0) return undefined
@@ -129,6 +131,20 @@ const composeValidate = (vld = [], valueField = {}) => value => {
 
       return validatorFn
     })
+
+    if(errors.length === 0 && fieldName === 'submission.articleURL') {
+      return client.query({
+        query: VALIDATE_DOI, 
+        variables: {
+          articleURL: value
+        }
+      }).then(res => {
+        if (!res.data.validateDOI.isDOIValid) {
+          return 'DOI is invalid'
+        }
+        return undefined
+      })
+    }
   return errors.length > 0 ? errors[0] : undefined
 }
 
@@ -150,6 +166,7 @@ const FormTemplate = ({
   validateForm,
   match,
 }) => {
+  const client = useApolloClient()
   const submitButton = text => (
     <div>
       <Button
@@ -256,6 +273,8 @@ const FormTemplate = ({
                   validate={composeValidate(
                     element.validate,
                     element.validateValue,
+                    element.name,
+                    client
                   )}
                   values={values}
                 />
diff --git a/app/queries/index.js b/app/queries/index.js
index d4b8427653b96a29005d1b54cfa91eecef90ecaa..8b36453fbb992089937e1b520c04b4836217a40e 100644
--- a/app/queries/index.js
+++ b/app/queries/index.js
@@ -83,6 +83,18 @@ export const SEARCH_USERS = gql`
   }
 `
 
+export const VALIDATE_DOI = gql`
+  query Manuscripts(
+    $articleURL: String
+  ) {
+    validateDOI(
+      articleURL: $articleURL
+    ){
+      isDOIValid
+    }
+  }
+`
+
 export const GET_MANUSCRIPTS = gql`
   query Manuscripts(
     $sort: String
diff --git a/app/storage/forms-ncrc/submit.json b/app/storage/forms-ncrc/submit.json
index 5a0f0fcb2af05620377097cbfb3a34f3d1348153..cba65b43a009367cf62364c9fdc24bf1b12e3b1a 100644
--- a/app/storage/forms-ncrc/submit.json
+++ b/app/storage/forms-ncrc/submit.json
@@ -224,7 +224,7 @@
         ]
       }
     ],
-    "id": "NCRC Submission Form",
+    "id": "submit",
     "name": "NCRC Submission Form",
     "description": "<p>NCRC Form</p>",
     "haspopup": "false"
diff --git a/config/permissions.js b/config/permissions.js
index 27b697bc679d532c8984047d2fec2c72561ee768..a2c154abd0484ee6db92313f30a4a1722dfb5114 100644
--- a/config/permissions.js
+++ b/config/permissions.js
@@ -307,6 +307,7 @@ const permissions = {
     getForm: allow,
     getForms: allow,
     user: allow,
+    validateDOI: allow,
   },
   Mutation: {
     upload: isAuthenticated,
diff --git a/cypress.json b/cypress.json
index 44964024078a78571666740a915b8224e2a7f55b..542f48538fb4b71b717e96109488e444e5c61c5b 100644
--- a/cypress.json
+++ b/cypress.json
@@ -1,5 +1,4 @@
 {
   "baseUrl": "http://localhost:4000",
-  "nodeVersion": "system",
-  "defaultCommandTimeout": 8000
+  "nodeVersion": "system"
 }
diff --git a/cypress/integration/elife/form_builer_page_spec.js b/cypress/integration/elife/form_builer_page_spec.js
index 01954de09f7a8fb708526272290709ac5b56e705..4c366a597d4c3a43adb024797be0d912798c6565 100644
--- a/cypress/integration/elife/form_builer_page_spec.js
+++ b/cypress/integration/elife/form_builer_page_spec.js
@@ -1,3 +1,4 @@
+/* eslint-disable jest/expect-expect */
 import { FormsPage } from '../../page-object/forms-page'
 import { NewSubmissionPage } from '../../page-object/new-submission-page'
 import { SubmissionFormPage } from '../../page-object/submission-form-page'
@@ -15,6 +16,7 @@ describe('Form builder page tests', () => {
       cy.fixture('role_names').then(name => {
         cy.login(name.role.admin, formBuilder)
       })
+      FormsPage.verifyPageLoaded()
     })
 
     it('check title and elements from form builder', () => {
@@ -78,8 +80,9 @@ describe('Form builder page tests', () => {
       cy.fixture('role_names').then(name => {
         cy.login(name.role.admin, manuscripts)
       })
+      ManuscriptsPage.getTableHeader().should('be.visible')
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
     })
 
     // check if the form contain all the columns
diff --git a/cypress/integration/elife/manuscripts_page_spec.js b/cypress/integration/elife/manuscripts_page_spec.js
index a1fa7eda6dcce9bba37e1d8a8511b476e10f42a3..58bc7afbc47070e735ce6c387025a3b17eb775b5 100644
--- a/cypress/integration/elife/manuscripts_page_spec.js
+++ b/cypress/integration/elife/manuscripts_page_spec.js
@@ -1,3 +1,4 @@
+/* eslint-disable jest/expect-expect */
 import { ManuscriptsPage } from '../../page-object/manuscripts-page'
 import { NewSubmissionPage } from '../../page-object/new-submission-page'
 import { SubmissionFormPage } from '../../page-object/submission-form-page'
@@ -15,6 +16,7 @@ describe('Manuscripts page tests', () => {
       cy.fixture('role_names').then(name => {
         cy.login(name.role.admin, manuscripts)
       })
+      ManuscriptsPage.getTableHeader().should('be.visible')
     })
 
     it('check Submit button is visible', () => {
@@ -22,13 +24,13 @@ describe('Manuscripts page tests', () => {
     })
     it('evaluation button is visible on unsubmited status article', () => {
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       // fill the submit form and submit it
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('submission_form_data').then(data => {
         SubmissionFormPage.fillInArticleld(data.articleId)
       })
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
       ManuscriptsPage.getEvaluationButton().should('be.visible')
     })
   })
@@ -43,28 +45,26 @@ describe('Manuscripts page tests', () => {
       cy.fixture('role_names').then(name => {
         cy.login(name.role.admin, manuscripts)
       })
+      ManuscriptsPage.getTableHeader().should('be.visible')
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       // fill the submit form and submit it
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('submission_form_data').then(data => {
         SubmissionFormPage.fillInArticleld(data.articleId)
       })
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
     })
 
-    it('unsubmited article is evaluated', () => {
+    it('unsubmitted article is evaluated', () => {
       ManuscriptsPage.clickEvaluation()
       cy.url().should('contain', 'evaluation')
 
-      // SubmissionFormPage.getArticleld().should('have.value', '')
       SubmissionFormPage.getArticleUrl().should('have.value', '')
-      // eslint-disable-next-line
       SubmissionFormPage.getDescription().should('have.value', '')
       SubmissionFormPage.getEvaluationContent()
         .find('p')
         .should('have.value', '')
-      // eslint-disable-next-line
       SubmissionFormPage.getFormOptionValue(-1).should('have.value', '')
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('submission_form_data').then(data => {
@@ -75,24 +75,24 @@ describe('Manuscripts page tests', () => {
         SubmissionFormPage.clickElementFromFormOptionList(4)
         SubmissionFormPage.selectDropdownOption(1)
         SubmissionFormPage.clickSubmitResearch()
-        SubmissionFormPage.clickSubmitManuscript()
+        SubmissionFormPage.clickSubmitManuscriptAndWaitPageLoad()
       })
       ManuscriptsPage.getStatus(0).should('eq', 'evaluated')
     })
 
     it('sort article after Article id', () => {
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       SubmissionFormPage.fillInArticleld('456')
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       SubmissionFormPage.fillInArticleld('abc')
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       SubmissionFormPage.fillInArticleld('def')
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
       ManuscriptsPage.getArticleTitleByRow(0).should('contain', 'def')
       ManuscriptsPage.getArticleTitleByRow(1).should('contain', 'abc')
       ManuscriptsPage.getArticleTitleByRow(2).should('contain', '456')
@@ -105,7 +105,7 @@ describe('Manuscripts page tests', () => {
     })
   })
 
-  context('Submited and evaluated article tests', () => {
+  context('Submitted and evaluated article tests', () => {
     beforeEach(() => {
       // task to restore the database as per the  dumps/initialState.sql
       cy.task('restore', 'initialState')
@@ -115,10 +115,11 @@ describe('Manuscripts page tests', () => {
       cy.fixture('role_names').then(name => {
         cy.login(name.role.admin, manuscripts)
 
+        ManuscriptsPage.getTableHeader().should('be.visible')
         ManuscriptsPage.getEvaluationButton().should('not.exist')
         ManuscriptsPage.clickSubmit()
 
-        NewSubmissionPage.clickSubmitURL()
+        NewSubmissionPage.clickSubmitUrlAndVerifyLink()
 
         // fill the submit form and submit it
         // eslint-disable-next-line jest/valid-expect-in-promise
@@ -131,9 +132,8 @@ describe('Manuscripts page tests', () => {
           SubmissionFormPage.selectDropdownOption(1)
           SubmissionFormPage.fillInCreator(name.role.admin)
           // eslint-disable-next-line
-          cy.wait(2000)
           SubmissionFormPage.clickSubmitResearch()
-          SubmissionFormPage.clickSubmitManuscript()
+          SubmissionFormPage.clickSubmitManuscriptAndWaitPageLoad()
         })
       })
     })
@@ -146,16 +146,16 @@ describe('Manuscripts page tests', () => {
     })
     it('evaluate article and check status is changed', () => {
       ManuscriptsPage.getStatus(0).should('eq', 'Submitted')
-      ManuscriptsPage.clickEvaluation(0)
+      ManuscriptsPage.clickEvaluation()
 
-      SubmissionFormPage.clickSubmitResearch()
+      SubmissionFormPage.clickSubmitResearchAndWaitPageLoad()
 
       ManuscriptsPage.getStatus(0).should('eq', 'evaluated')
       ManuscriptsPage.getEvaluationButton().should('be.visible')
     })
     it('submission details should be visible', () => {
       ManuscriptsPage.getStatus(0).should('eq', 'Submitted')
-      ManuscriptsPage.clickEvaluation(0)
+      ManuscriptsPage.clickEvaluation()
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('submission_form_data').then(data => {
         SubmissionFormPage.getArticleld().should('have.value', data.articleId)
@@ -183,7 +183,7 @@ describe('Manuscripts page tests', () => {
           ManuscriptsPage.getAuthor(0).should('eq', name.role.admin)
           ManuscriptsPage.getStatus(0).should('eq', 'Submitted')
         })
-        ManuscriptsPage.clickEvaluation(0)
+        ManuscriptsPage.clickEvaluation()
         SubmissionFormPage.fillInArticleld('123 - Evaluated')
         SubmissionFormPage.fillInArticleUrl('new url')
         SubmissionFormPage.fillInDescription('new description')
@@ -192,8 +192,7 @@ describe('Manuscripts page tests', () => {
         SubmissionFormPage.selectDropdownOption(-1)
         SubmissionFormPage.fillInCreator('creator')
         // eslint-disable-next-line
-        cy.wait(2000)
-        SubmissionFormPage.clickSubmitResearch()
+        SubmissionFormPage.clickSubmitResearchAndWaitPageLoad()
         ManuscriptsPage.clickEvaluation()
         // eslint-disable-next-line
         SubmissionFormPage.getArticleld().should(
diff --git a/cypress/integration/ncrc/form_builder_page_spec.js b/cypress/integration/ncrc/form_builder_page_spec.js
index 9bba6426e16ad89031e66f343521b6b4ba4429ea..539b56d6f94d196429594ec79bdf785849f5a2d7 100644
--- a/cypress/integration/ncrc/form_builder_page_spec.js
+++ b/cypress/integration/ncrc/form_builder_page_spec.js
@@ -1,3 +1,4 @@
+/* eslint-disable jest/expect-expect */
 import { formBuilder, manuscripts } from '../../support/routes'
 import { FormsPage } from '../../page-object/forms-page'
 import { ManuscriptsPage } from '../../page-object/manuscripts-page'
@@ -14,6 +15,7 @@ describe('form builder tests', () => {
       cy.fixture('role_names').then(name => {
         cy.login(name.role.admin, formBuilder)
       })
+      FormsPage.verifyPageLoaded()
     })
     it('check form entries are correct', () => {
       // eslint-disable-next-line jest/valid-expect-in-promise
@@ -84,8 +86,9 @@ describe('form builder tests', () => {
       cy.fixture('role_names').then(name => {
         cy.login(name.role.admin, manuscripts)
       })
+      ManuscriptsPage.getTableHeader().should('be.visible')
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
     })
 
     it('check submission form contains the same fields', () => {
diff --git a/cypress/integration/ncrc/manuscripts_page_spec.js b/cypress/integration/ncrc/manuscripts_page_spec.js
index a581d239c5f2869dd540a8155ac4b876162303d8..60288f3044726f74467b0087634f0d01456a4ef1 100644
--- a/cypress/integration/ncrc/manuscripts_page_spec.js
+++ b/cypress/integration/ncrc/manuscripts_page_spec.js
@@ -1,3 +1,4 @@
+/* eslint-disable jest/expect-expect */
 import { manuscripts } from '../../support/routes'
 import { ManuscriptsPage } from '../../page-object/manuscripts-page'
 import { NewSubmissionPage } from '../../page-object/new-submission-page'
@@ -14,6 +15,7 @@ describe('manuscripts page tests', () => {
     cy.fixture('role_names').then(name => {
       cy.login(name.role.admin, manuscripts)
     })
+    ManuscriptsPage.getTableHeader().should('be.visible')
   })
   context('elements visibility', () => {
     it('submit button should be visible & dashboard page should not exist', () => {
@@ -23,20 +25,20 @@ describe('manuscripts page tests', () => {
 
     it('evaluation button should be visible for unsubmitted articles', () => {
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       // fill the submit form and submit it
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('submission_form_data').then(data => {
         SubmissionFormPage.fillInArticleDescription(data.articleId)
       })
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
       ManuscriptsPage.getEvaluationButton()
         .scrollIntoView()
         .should('be.visible')
     })
     it('label & topics should be visible on manuscripts page', () => {
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       // fill the submit form and submit it
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('submission_form_data').then(data => {
@@ -44,7 +46,7 @@ describe('manuscripts page tests', () => {
         SubmissionFormPage.selectDropdownOption(1)
         SubmissionFormPage.clickTopicsCheckboxWithText(data.topic)
         SubmissionFormPage.clickTopicsCheckboxWithText('epidemiology')
-        Menu.clickManuscripts()
+        Menu.clickManuscriptsAndAssertPageLoad()
         ManuscriptsPage.getArticleLabel()
           .should('be.visible')
           .and('contain', 'evaluated')
@@ -56,7 +58,7 @@ describe('manuscripts page tests', () => {
   context('unsubmitted article tests', () => {
     beforeEach(() => {
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
     })
 
     it('unsubmitted article is evaluated', () => {
@@ -64,7 +66,7 @@ describe('manuscripts page tests', () => {
       cy.fixture('submission_form_data').then(data => {
         SubmissionFormPage.fillInArticleDescription(data.title)
       })
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
       ManuscriptsPage.clickEvaluation()
       cy.url().should('contain', 'evaluation')
       SubmissionFormPage.getArticleUrl().should('have.value', '')
@@ -92,7 +94,7 @@ describe('manuscripts page tests', () => {
         SubmissionFormPage.clickDropdown(-1)
         SubmissionFormPage.selectDropdownOption(0)
         SubmissionFormPage.clickTopicsCheckboxWithText(data.topic)
-        SubmissionFormPage.clickSubmitManuscript()
+        SubmissionFormPage.clickSubmitManuscriptAndWaitPageLoad()
         ManuscriptsPage.getStatus(0).should('eq', 'evaluated')
         ManuscriptsPage.getArticleTopic(0)
           .should('be.visible')
@@ -104,7 +106,7 @@ describe('manuscripts page tests', () => {
   context('submitted and evaluated article tests', () => {
     beforeEach(() => {
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       // fill the submit form and submit it
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('submission_form_data').then(data => {
@@ -122,8 +124,8 @@ describe('manuscripts page tests', () => {
         SubmissionFormPage.selectDropdownOption(0)
         SubmissionFormPage.clickTopicsCheckboxWithText(data.topic)
         // eslint-disable-next-line
-        cy.wait(2000)
-        SubmissionFormPage.clickSubmitManuscript()
+        SubmissionFormPage.waitThreeSec()
+        SubmissionFormPage.clickSubmitManuscriptAndWaitPageLoad()
       })
     })
     it('manuscripts page should contain the correct details after submission', () => {
@@ -147,9 +149,9 @@ describe('manuscripts page tests', () => {
     })
     it('evaluate article and check status is changed', () => {
       ManuscriptsPage.getStatus(0).should('eq', 'Submitted')
-      ManuscriptsPage.clickEvaluation(0)
+      ManuscriptsPage.clickEvaluation()
 
-      SubmissionFormPage.clickSubmitManuscript()
+      SubmissionFormPage.clickSubmitManuscriptAndWaitPageLoad()
 
       ManuscriptsPage.getStatus(0).should('eq', 'evaluated')
     })
@@ -157,12 +159,12 @@ describe('manuscripts page tests', () => {
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('submission_form_data').then(data => {
         ManuscriptsPage.getStatus(0).should('eq', 'Submitted')
-        ManuscriptsPage.clickEvaluation(0)
+        ManuscriptsPage.clickEvaluation()
         SubmissionFormPage.fillInValueAdded('Evaluated')
         SubmissionFormPage.clickTopicsCheckboxWithText('vaccines')
         // eslint-disable-next-line
-        cy.wait(2500)
-        SubmissionFormPage.clickSubmitManuscript()
+        SubmissionFormPage.waitThreeSec()
+        SubmissionFormPage.clickSubmitManuscriptAndWaitPageLoad()
         ManuscriptsPage.clickEvaluation()
         // eslint-disable-next-line
         SubmissionFormPage.getValueAddedField().should(
@@ -176,7 +178,7 @@ describe('manuscripts page tests', () => {
   context('filter and sort articles', () => {
     beforeEach(() => {
       ManuscriptsPage.clickSubmit()
-      NewSubmissionPage.clickSubmitURL()
+      NewSubmissionPage.clickSubmitUrlAndVerifyLink()
       // eslint-disable-next-line jest/valid-expect-in-promise
       cy.fixture('form_option').then(data => {
         SubmissionFormPage.fillInArticleDescription('123')
@@ -185,9 +187,9 @@ describe('manuscripts page tests', () => {
         SubmissionFormPage.clickTopicsCheckboxWithText(
           data.ncrc.topicTypes.vaccines,
         )
-        Menu.clickManuscripts()
+        Menu.clickManuscriptsAndAssertPageLoad()
         ManuscriptsPage.clickSubmit()
-        NewSubmissionPage.clickSubmitURL()
+        NewSubmissionPage.clickSubmitUrlAndVerifyLink()
         SubmissionFormPage.fillInArticleDescription('abc')
         SubmissionFormPage.clickElementFromFormOptionList(9)
         SubmissionFormPage.selectDropdownOption(1)
@@ -197,9 +199,9 @@ describe('manuscripts page tests', () => {
         SubmissionFormPage.clickTopicsCheckboxWithText(
           data.ncrc.topicTypes.diagnostics,
         )
-        Menu.clickManuscripts()
+        Menu.clickManuscriptsAndAssertPageLoad()
         ManuscriptsPage.clickSubmit()
-        NewSubmissionPage.clickSubmitURL()
+        NewSubmissionPage.clickSubmitUrlAndVerifyLink()
         SubmissionFormPage.fillInArticleDescription('def')
         SubmissionFormPage.clickElementFromFormOptionList(9)
         SubmissionFormPage.selectDropdownOption(0)
@@ -209,9 +211,8 @@ describe('manuscripts page tests', () => {
         SubmissionFormPage.clickTopicsCheckboxWithText(
           data.ncrc.topicTypes.diagnostics,
         )
-        Menu.clickManuscripts()
+        Menu.clickManuscriptsAndAssertPageLoad()
         // eslint-disable-next-line
-        cy.wait(2000)
       })
     })
     it('filter article after topic and url contain that topic', () => {
@@ -219,7 +220,7 @@ describe('manuscripts page tests', () => {
       ManuscriptsPage.getTableRows().should('eq', 1)
       cy.url().should('contain', 'vaccines')
       ManuscriptsPage.getArticleTopic(0).should('contain', 'vaccines')
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
       ManuscriptsPage.clickArticleTopic(1)
       ManuscriptsPage.getTableRows().should('eq', 2)
       cy.url().should('contain', 'diagnostics')
@@ -230,7 +231,7 @@ describe('manuscripts page tests', () => {
         'ecology and spillover',
       )
       ManuscriptsPage.getArticleTopic(3).should('contain', 'diagnostics')
-      Menu.clickManuscripts()
+      Menu.clickManuscriptsAndAssertPageLoad()
       ManuscriptsPage.clickArticleTopic(0)
       ManuscriptsPage.getTableRows().should('eq', 1)
       cy.url().should('contain', 'modeling')
diff --git a/cypress/page-object/forms-page.js b/cypress/page-object/forms-page.js
index 9bc8175e9ad4e74c1b6d64aa99a38b0009661dc8..9cdd4ea3e050569a67ea3c83f25f9a77051eb574 100644
--- a/cypress/page-object/forms-page.js
+++ b/cypress/page-object/forms-page.js
@@ -3,37 +3,41 @@
  * Page object representing the second option from the left side menu,
  * which contains a number of options & fields to fill in / dropdowns.
  */
-const FORM_TITLE_TAB = '[data-test-id="tab-container"] > div';
-const FORM_OPTION_LIST = '[class*=FormBuilder__Element] > button:nth-child(1)';
-const NAME_FIELD = 'name';
+const FORM_TITLE_TAB = '[data-test-id="tab-container"] > div'
+const FORM_OPTION_LIST = '[class*=FormBuilder__Element] > button:nth-child(1)'
+const NAME_FIELD = 'name'
 const COMPONENT_TYPE = '[role=listbox]'
 const FIELD_VALIDATE = '[class*=react-select__value-container]'
 
 export const FormsPage = {
-    getFormTitleTab(nth) {
-        return cy.get(FORM_TITLE_TAB).eq(nth);
-    },
-    getFormTitleText(nth) {
-        this.getFormTitleTab(nth).invoke('text');
-    },
-    getFormOptionList() {
-        return cy.get(FORM_OPTION_LIST);
-    },
-    clickFormOption(nth) {
-        this.getFormOptionList().eq(nth).click();
-    },
+  verifyPageLoaded() {
+    return cy.get(FORM_TITLE_TAB, { timeout: 10000 }).eq(0).should('be.visible')
+  },
+  getFormTitleTab(nth) {
+    return cy.get(FORM_TITLE_TAB).eq(nth)
+  },
+  getFormTitleText(nth) {
+    this.getFormTitleTab(nth).invoke('text')
+  },
+  getFormOptionList() {
+    return cy.get(FORM_OPTION_LIST)
+  },
+  clickFormOption(nth) {
+    this.getFormOptionList().eq(nth).click()
+  },
 
-
-    getFormBuilderElementName(nth) {
-        return cy.get(FORM_OPTION_LIST).eq(nth)
-    },
-    getNameField() {
-        return cy.getByName(NAME_FIELD);
-    },
-    getComponentType() {
-        return cy.get(COMPONENT_TYPE);
-    },
-    getFieldValidate() {
-        return cy.get(FIELD_VALIDATE)
-    }
+  getFormBuilderElementName(nth) {
+    return cy.get(FORM_OPTION_LIST).eq(nth)
+  },
+  getNameField() {
+    return cy.getByName(NAME_FIELD)
+  },
+  getComponentType() {
+    return cy.get(COMPONENT_TYPE)
+  },
+  getFieldValidate() {
+    return cy.get(FIELD_VALIDATE)
+  },
 }
+
+export default FormsPage
diff --git a/cypress/page-object/manuscripts-page.js b/cypress/page-object/manuscripts-page.js
index 5532db84321d02b1bc4c0e5eb32db5ad1b99c3bd..4844234241a06e04a035c566904a272e4ebccffc 100644
--- a/cypress/page-object/manuscripts-page.js
+++ b/cypress/page-object/manuscripts-page.js
@@ -1,4 +1,6 @@
 /// <reference types="Cypress" />
+import { evaluate } from '../support/routes'
+
 /**
  * Page component representing the fourth option in the left side menu,
  * where users can see the list of submitted manuscripts & select Control,
@@ -12,6 +14,7 @@ const CONTROL_BUTTON = '[href*=control]'
 const CREATED_CARET = 'Carets__Caret'
 const AUTHOR_FIELD = 'UserCombo__Primary'
 const STATUS_FIELD = 'Badge__Status'
+const TABLE_HEADER = '[class*=Table__Header]'
 const MANUSCRIPTS_TABLE_HEAD = '[class*=Table__Header] > tr >th'
 const ARTICLE_TITLE = '[class*=Table__Row]>td:nth-child(1)'
 const ARTICLE_LABEL = 'style__StyledTableLabel'
@@ -44,6 +47,10 @@ export const ManuscriptsPage = {
   clickEvaluation() {
     this.getEvaluationButton().click()
   },
+  clickEvaluationAndVerifyUrl() {
+    this.clickEvaluation()
+    cy.url({ timeout: 10000 }).should('contain', evaluate)
+  },
   getControlButton() {
     return cy.get(CONTROL_BUTTON)
   },
@@ -86,5 +93,8 @@ export const ManuscriptsPage = {
   getLabelRow(nth) {
     return cy.get(LABEL).eq(nth)
   },
+  getTableHeader() {
+    return cy.get(TABLE_HEADER, { timeout: 10000 })
+  },
 }
 export default ManuscriptsPage
diff --git a/cypress/page-object/new-submission-page.js b/cypress/page-object/new-submission-page.js
index 754e8c96ce8db52b530031f0c2270231999fc04c..14382a20df50125774045b5adf931bd4d0b1486e 100644
--- a/cypress/page-object/new-submission-page.js
+++ b/cypress/page-object/new-submission-page.js
@@ -1,26 +1,34 @@
 /// <reference types="Cypress" />
+import { submit } from '../support/routes'
+
 /**
  * Page object representing the available submission options:
  * submission through uploading a manuscript or through using an URL.
  */
-const UPLOAD_MANUSCRIPT_BUTTON = 'UploadManuscript__Info';
-const SUBMIT_URL_BUTTON = 'button';
-const SUBMISSION_MESSAGE = 'body';
+const UPLOAD_MANUSCRIPT_BUTTON = 'UploadManuscript__Info'
+const SUBMIT_URL_BUTTON = 'button'
+const SUBMISSION_MESSAGE = 'body'
 
 export const NewSubmissionPage = {
-    getUploadManuscriptButton() {
-        return cy.getByContainsClass(UPLOAD_MANUSCRIPT_BUTTON);
-    },
-    clickUploadManuscript() {
-        this.getUploadManuscriptButton().click();
-    },
-    getSubmitURLButton() {
-        return cy.get(SUBMIT_URL_BUTTON);
-    },
-    clickSubmitURL() {
-        this.getSubmitURLButton().click();
-    },
-    getSubmissionMessage() {
-        return cy.get(SUBMISSION_MESSAGE).invoke('text');
-    }
-}
\ No newline at end of file
+  getUploadManuscriptButton() {
+    return cy.getByContainsClass(UPLOAD_MANUSCRIPT_BUTTON)
+  },
+  clickUploadManuscript() {
+    this.getUploadManuscriptButton().click()
+  },
+  getSubmitURLButton() {
+    return cy.get(SUBMIT_URL_BUTTON)
+  },
+  clickSubmitURL() {
+    this.getSubmitURLButton().click()
+  },
+  clickSubmitUrlAndVerifyLink() {
+    this.clickSubmitURL()
+    cy.url({ timeout: 15000 }).should('contain', submit)
+  },
+  getSubmissionMessage() {
+    return cy.get(SUBMISSION_MESSAGE).invoke('text')
+  },
+}
+
+export default NewSubmissionPage
diff --git a/cypress/page-object/page-component/menu.js b/cypress/page-object/page-component/menu.js
index da43dc01378cfc93382dcd9a0032a29589c26fb7..b4ae870e4b311f93ba794b3bfaa9c83cf7cfa63b 100644
--- a/cypress/page-object/page-component/menu.js
+++ b/cypress/page-object/page-component/menu.js
@@ -1,4 +1,6 @@
 /// <reference types="Cypress" />
+import { ManuscriptsPage } from '../manuscripts-page'
+
 /**
  * Page component which represents the left side menu bar,
  * which contains the Logged User, Dashboard & My profile options (for non-admin users),
@@ -34,6 +36,10 @@ export const Menu = {
   clickManuscripts() {
     this.getManuscriptsButton().click()
   },
+  clickManuscriptsAndAssertPageLoad() {
+    this.clickManuscripts()
+    ManuscriptsPage.getTableHeader().should('be.visible')
+  },
   getMyProfileButton() {
     return cy.getByContainsClass(MENU_BUTTON).contains('My profile')
   },
diff --git a/cypress/page-object/submission-form-page.js b/cypress/page-object/submission-form-page.js
index 29e97db337553599b5700c3285d6d7c0cf64b1ac..0c2408ab7c8d23330a6f3362e8b0fde3265bf4e1 100644
--- a/cypress/page-object/submission-form-page.js
+++ b/cypress/page-object/submission-form-page.js
@@ -1,4 +1,6 @@
 /// <reference types="Cypress" />
+import { ManuscriptsPage } from './manuscripts-page'
+
 /**
  * Page object representing the form which has
  * to be completed to correctly submit a research paper.
@@ -211,12 +213,20 @@ export const SubmissionFormPage = {
   clickSubmitResearch() {
     this.getSubmitResearchButton().click()
   },
+  clickSubmitResearchAndWaitPageLoad() {
+    this.clickSubmitResearch()
+    ManuscriptsPage.getTableHeader().should('be.visible')
+  },
   getSubmitManuscriptButton() {
     return cy.get(SUBMIT_MANUSCRIPT_BUTTON)
   },
   clickSubmitManuscript() {
     this.getSubmitManuscriptButton().click()
   },
+  clickSubmitManuscriptAndWaitPageLoad() {
+    this.clickSubmitManuscript()
+    ManuscriptsPage.getTableHeader().should('be.visible')
+  },
   getValidationErrorMessage(error) {
     return cy.getByContainsClass(VALIDATION_ERROR_MESSAGE).contains(error)
   },
@@ -336,5 +346,9 @@ export const SubmissionFormPage = {
   clickTopicsCheckboxWithText(value) {
     this.getTopicsCheckboxWithText(value).click()
   },
+  waitThreeSec() {
+    // eslint-disable-next-line cypress/no-unnecessary-waiting
+    cy.wait(3000)
+  },
 }
 export default SubmissionFormPage
diff --git a/cypress/support/routes.js b/cypress/support/routes.js
index 6cdab8f2e789701f5b1cce447ef3a83350da3284..3080992947f37f10d444c0e8b34026b18abe893f 100644
--- a/cypress/support/routes.js
+++ b/cypress/support/routes.js
@@ -4,3 +4,5 @@ export const manuscripts = '/kotahi/admin/manuscripts'
 export const formBuilder = '/kotahi/admin/form-builder'
 export const users = '/kotahi/admin/users'
 export const profile = '/kotahi/profile'
+export const submit = '/submit'
+export const evaluate = '/evaluate'
diff --git a/docker-compose.production.elife.yml b/docker-compose.production.elife.yml
index 5b94f49d7b1c6180d7f8c1392f4abd196119b47e..1a4f1d40fe09ac15a100455e587bbfe21f938814 100644
--- a/docker-compose.production.elife.yml
+++ b/docker-compose.production.elife.yml
@@ -52,7 +52,7 @@ services:
       - ./db-data:/var/lib/postgresql/data/kotahi:z
 
   job-xsweet:
-    image: pubsweet/job-xsweet:1.5.0
+    image: cokoapps/job-xsweet:1.5.1
     environment:
       - DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-db}:${POSTGRES_PORT}/${POSTGRES_DB}
     depends_on:
diff --git a/docker-compose.production.ncrc.yml b/docker-compose.production.ncrc.yml
index 49a5c276595b0a55dbdcf8081d7e58f5f8a91e2a..3a16954d6791a96fbfcec28af5a01d2750a72d28 100644
--- a/docker-compose.production.ncrc.yml
+++ b/docker-compose.production.ncrc.yml
@@ -52,7 +52,7 @@ services:
       - ./db-data:/var/lib/postgresql/data/kotahi:z
 
   job-xsweet:
-    image: pubsweet/job-xsweet:1.5.0
+    image: cokoapps/job-xsweet:1.5.1
     environment:
       - DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-db}:${POSTGRES_PORT}/${POSTGRES_DB}
     depends_on:
diff --git a/docker-compose.production.yml b/docker-compose.production.yml
index 3063a8f3eab9834baed91221165612bf883c508d..66b721686697f071c124016eea6ff0fc434594a4 100644
--- a/docker-compose.production.yml
+++ b/docker-compose.production.yml
@@ -38,7 +38,7 @@ services:
       - INSTANCE_NAME=${INSTANCE_NAME:-elife}
 
   job-xsweet:
-    image: pubsweet/job-xsweet:1.5.0
+    image: cokoapps/job-xsweet:1.5.1
     depends_on:
       - server
     command:
diff --git a/docker-compose.yml b/docker-compose.yml
index 7205cf7ea55d376b479e4bcdeea91f8c07ab2670..46b3caca50cb293b734833932e425354676b105a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -80,7 +80,7 @@ services:
       - ./server:/home/node/app/server
 
   job-xsweet:
-    image: pubsweet/job-xsweet:1.5.0
+    image: cokoapps/job-xsweet:1.5.1
     depends_on:
       - server
     command:
diff --git a/package.json b/package.json
index 0fff8235ed1551ca29394a458eeace6b9564bf36..51023f5980ab7c2eb7380403da5aab345cdf3d51 100644
--- a/package.json
+++ b/package.json
@@ -99,7 +99,7 @@
     "@pubsweet/base-model": "4.0.0",
     "@pubsweet/component-send-email": "0.4.3",
     "@pubsweet/errors": "^2.0.40",
-    "@pubsweet/job-xsweet": "^2.1.11",
+    "@pubsweet/job-xsweet": "^2.2.0",
     "@pubsweet/logger": "^0.2.50",
     "@pubsweet/model-user": "6.0.3",
     "@pubsweet/models": "^0.3.15",
diff --git a/server/model-manuscript/src/graphql.js b/server/model-manuscript/src/graphql.js
index 2a1726f0144905bebb8ea9a2f236ff254594e5eb..7435cc6b387afa5fc4c5bf695cd154b6c8d12299 100644
--- a/server/model-manuscript/src/graphql.js
+++ b/server/model-manuscript/src/graphql.js
@@ -507,6 +507,23 @@ const resolvers = {
 
       // return ctx.connectors.User.fetchAll(where, ctx, { eager })
     },
+
+    async validateDOI(_, { articleURL }, ctx) {
+      const DOI = encodeURI(articleURL.split('.org/')[1])
+      try {
+        await axios.get(`https://api.crossref.org/works/${DOI}/agency`)
+
+        return {
+          isDOIValid: true
+        }
+      } catch(err) {
+        // eslint-disable-next-line
+        console.log(err)
+        return {
+          isDOIValid: false
+        }
+      }
+    }
   },
   // We want submission into to come out as a stringified JSON, so that we don't have to
   // change our queries if the submission form changes. We still want to store it as JSONB
@@ -522,6 +539,7 @@ const typeDefs = `
     manuscripts: [Manuscript]!
     paginatedManuscripts(sort: String, offset: Int, limit: Int, filter: ManuscriptsFilter): PaginatedManuscripts
     publishedManuscripts(sort:String, offset: Int, limit: Int): PaginatedManuscripts
+    validateDOI(articleURL: String): validateDOIResponse
   }
 
   input ManuscriptsFilter {
@@ -529,6 +547,10 @@ const typeDefs = `
     submission: String
   }
 
+  type validateDOIResponse {
+    isDOIValid: Boolean
+  }
+
   type PaginatedManuscripts {
     totalCount: Int
     manuscripts: [Manuscript]
diff --git a/yarn.lock b/yarn.lock
index 045d9b3f6a28e069be9e1a0b3eaac2a7c27518ec..7352381b11c60f60bcf30f5b5798859da1ba5c26 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2509,6 +2509,21 @@
     tmp-promise "^2.0.0"
     umzug "^2.1.0"
 
+"@pubsweet/db-manager@^3.1.23":
+  version "3.1.23"
+  resolved "https://registry.yarnpkg.com/@pubsweet/db-manager/-/db-manager-3.1.23.tgz#0d5f814605419f1dc53d6b4f306c4bfd279a9e9a"
+  integrity sha512-/cJq4nntAV5EzuctXapktQqYtj7ywMbcrQt0XMN6QRE5XzBZYevDUGdZdp+2GKhdUG/8pEsPFjDeHMgi1xWbyg==
+  dependencies:
+    "@hapi/joi" "^14.3.0"
+    "@pubsweet/logger" "^0.2.58"
+    fs-extra "^8.1.0"
+    knex "^0.21.1"
+    lodash "^4.17.11"
+    objection "^2.1.3"
+    pg "^7.8.0"
+    tmp-promise "^2.0.0"
+    umzug "^2.1.0"
+
 "@pubsweet/errors@^2.0.36", "@pubsweet/errors@^2.0.40", "@pubsweet/errors@^2.0.44":
   version "2.0.44"
   resolved "https://registry.yarnpkg.com/@pubsweet/errors/-/errors-2.0.44.tgz#0270e4bc19702e79f5583f6161577a87536c57a1"
@@ -2516,16 +2531,16 @@
   dependencies:
     http-status-codes "^1.3.0"
 
-"@pubsweet/job-xsweet@^2.1.11":
-  version "2.1.20"
-  resolved "https://registry.yarnpkg.com/@pubsweet/job-xsweet/-/job-xsweet-2.1.20.tgz#325eac40ac7765c7060105875bab59749bea8ee3"
-  integrity sha512-B5XPFMJY6gXDNFaW5cUW3+SO8EkTk5SFLOI85pQtX1TBm6ek4HpnAAJC0BH09IYDV22LUg36SQPVZISLFj1Rog==
+"@pubsweet/job-xsweet@^2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@pubsweet/job-xsweet/-/job-xsweet-2.2.0.tgz#5a0c095cdb4a3232060555957a047701684d9411"
+  integrity sha512-yvYL9qkWZxDsFHCTFhShTPklox5PaR2Uz5DypkJWDHx4g91q/Hab9uDCBYKfQgjkFNoYN7jihz7FUHODbKzDsw==
   dependencies:
-    "@pubsweet/db-manager" "^3.1.19"
-    "@pubsweet/logger" "^0.2.54"
+    "@pubsweet/db-manager" "^3.1.23"
+    "@pubsweet/logger" "^0.2.58"
     express-fileupload v1.1.1-alpha.2
     node-wait-for-it "^0.2.0"
-    pubsweet-server "^13.12.3"
+    pubsweet-server "^13.12.7"
     tmp-promise "^2.0.0"
     waait "^1.0.5"
 
@@ -2537,6 +2552,14 @@
     "@hapi/joi" "^14.5.0"
     config "^3.0.1"
 
+"@pubsweet/logger@^0.2.58":
+  version "0.2.58"
+  resolved "https://registry.yarnpkg.com/@pubsweet/logger/-/logger-0.2.58.tgz#baab6a6f627e0861fd8d485f5f34fe92ffdca68c"
+  integrity sha512-8KmpEnEeqKLWWANcYbFyjEt/AHUwHVvJ/mMq3AYLPuwNmr2oifxfO3aps3YOFzAAdH3ayvhOXrL7P5D/HJkctg==
+  dependencies:
+    "@hapi/joi" "^14.5.0"
+    config "^3.0.1"
+
 "@pubsweet/model-user@6.0.3":
   version "6.0.3"
   resolved "https://registry.yarnpkg.com/@pubsweet/model-user/-/model-user-6.0.3.tgz#b0ad3e307e293d9794f20bcc3797164732eb221b"
@@ -16444,7 +16467,7 @@ pubsweet-client@^10.2.5:
     styled-normalize "^8.0.6"
     subscriptions-transport-ws "^0.9.12"
 
-pubsweet-server@13.12.3, pubsweet-server@^13.11.6, pubsweet-server@^13.12.3:
+pubsweet-server@13.12.3, pubsweet-server@^13.11.6, pubsweet-server@^13.12.3, pubsweet-server@^13.12.7:
   version "13.12.3"
   resolved "https://registry.yarnpkg.com/pubsweet-server/-/pubsweet-server-13.12.3.tgz#f5fade9667000d2be8bc448f15b1df9a89fb58b2"
   integrity sha512-tl3LemBnFBBo5x/UucwevGDBYb2Jg7mwcYaxQ4tkzVHCoFqC9BB5wx9qGVHM9EwcwhiNMK+vqmzvDClFZrrbvw==