diff --git a/app/plugins/CoreEnroller/config/routes.php b/app/plugins/CoreEnroller/config/routes.php
new file mode 100644
index 00000000..1cfe2b47
--- /dev/null
+++ b/app/plugins/CoreEnroller/config/routes.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * ApiSource plugin specific routes.
+ *
+ * Portions licensed to the University Corporation for Advanced Internet
+ * Development, Inc. ("UCAID") under one or more contributor license agreements.
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @link          https://www.internet2.edu/comanage COmanage Project
+ * @package       registry-plugins
+ * @since         COmanage Registry v5.0.0
+ * @license       Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ */
+
+use Cake\Routing\Route\DashedRoute;
+
+// In general, we're probably trying to set up API routes if we're doing
+// something within a plugin, but not necessarily. API routes are a subset
+// of Cake routes, so either can be specified here.
+
+// Core Enroller
+
+
+$routes->plugin(
+  'CoreEnroller',
+  ['path' => '/core-enroller/'],
+  function ($routes) {
+    $routes->setRouteClass(DashedRoute::class);
+
+    $routes->get(
+      'email-verifiers/resend',
+      [
+        'plugin' => 'CoreEnroller',
+        'controller' => 'EmailVerifiers',
+        'action' => 'resend',
+      ])
+      ->setPass(['id'])
+      ->setPatterns(['id' => '[0-9]+']);
+  }
+);
\ No newline at end of file
diff --git a/app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po b/app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po
index 5a41e73c..461f50a1 100644
--- a/app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po
+++ b/app/plugins/CoreEnroller/resources/locales/en_US/core_enroller.po
@@ -98,7 +98,22 @@ msgid "information.EmailVerifiers.A"
 msgstr "The following email addresses have been found in this Petition. You must verify all of them in order to proceed to the next Enrollment Step."
 
 msgid "information.EmailVerifiers.code_sent"
-msgstr "A code has been sent to {0}. Please enter it below. You may also request a new code if you haven't received it after a few minutes, or cancel verification and return to the list of available Email Addresses."
+msgstr "A code has been sent to <strong>{0}</strong>. Please enter it below. You may also request a new code if you haven't received it after a few minutes, or cancel verification and return to the list of available Email Addresses."
+
+msgid "information.EmailVerifiers.resend-pre-text"
+msgstr "Didn't receive the code?"
+
+msgid "information.EmailVerifiers.resend"
+msgstr "Resend"
+
+msgid "information.EmailVerifiers.sending"
+msgstr "Sending"
+
+msgid "information.EmailVerifiers.success"
+msgstr "New Code Submitted!"
+
+msgid "information.EmailVerifiers.abort"
+msgstr "Abort"
 
 msgid "field.AttributeCollectors.valid_through.default.after.desc"
 msgstr "Days After Finalization"
@@ -257,4 +272,7 @@ msgid "result.InvitationAccepters.declined"
 msgstr "Invitation Declined at {0}"
 
 msgid "result.InvitationAccepters.none"
-msgstr "No response to invitation yet"
\ No newline at end of file
+msgstr "No response to invitation yet"
+
+msgid "op.EmailVerifiers.verify"
+msgstr "Verify Email"
\ No newline at end of file
diff --git a/app/plugins/CoreEnroller/src/Controller/EmailVerifiersController.php b/app/plugins/CoreEnroller/src/Controller/EmailVerifiersController.php
index 23dd271d..52c4ec62 100644
--- a/app/plugins/CoreEnroller/src/Controller/EmailVerifiersController.php
+++ b/app/plugins/CoreEnroller/src/Controller/EmailVerifiersController.php
@@ -29,11 +29,13 @@
 
 namespace CoreEnroller\Controller;
 
-use Cake\ORM\TableRegistry;
 use App\Controller\StandardEnrollerController;
 use App\Lib\Enum\PetitionStatusEnum;
 use App\Lib\Util\StringUtilities;
+use Cake\Http\Exception\BadRequestException;
+use Cake\ORM\TableRegistry;
 use CoreEnroller\Lib\Enum\VerificationModeEnum;
+use \App\Lib\Enum\HttpStatusCodesEnum;
 
 class EmailVerifiersController extends StandardEnrollerController {
   public $paginate = [
@@ -60,9 +62,58 @@ public function beforeRender(\Cake\Event\EventInterface $event) {
       $this->set('vv_bc_parent_primarykey', $this->EmailVerifiers->EnrollmentFlowSteps->getPrimaryKey());
     }
     
+    if ($this->getRequest()->getQuery("op") == "verify" || $this->getRequest()->getQuery("op") == "index") {
+      // This will suppress the default behavior. By default, we print the submit button in the
+      // unorderedList.php element. But for the verify view we want to override and customize
+      $this->set('suppress_submit', true);
+    }
+
     return parent::beforeRender($event);
   }
 
+  /**
+   * Resend the email verification request.
+   *
+   * @param string $id Email Verifier ID
+   * @throws BadRequestException If the request is not AJAX
+   * @throws \InvalidArgumentException If required query parameters are missing
+   * @return void
+   * @since COmanage Registry v5.1.0
+   */
+  public function resend($id)
+  {
+
+    $this->viewBuilder()->setClassName('Json');
+
+    if (!$this->getRequest()->is('ajax')) {
+      throw new BadRequestException(__('Bad Request'));
+    }
+
+    if (!$this->getRequest()->getQuery('petition_id') || !$this->getRequest()->getQuery('m')) {
+      throw new \InvalidArgumentException(__('error', 'invalid.request'));
+    }
+
+    // Generate a Verification request and send it
+    $Petitions = TableRegistry::getTableLocator()->get('Petitions');
+    $petition = $Petitions->get($this->getRequest()->getQuery('petition_id'));
+    $cfg = $this->EmailVerifiers->get($id);
+    $mail = StringUtilities::urlbase64decode($this->requestParam('m'));
+    $status = $this->EmailVerifiers->sendVerificationRequest($cfg, $petition, $mail, true);
+
+    if ($status) {
+      return $this->response
+        ->withType('application/json')
+        ->withStatus(HttpStatusCodesEnum::HTTP_OK)
+        ->withStringBody(json_encode(['status' => 'ok']));
+    }
+
+
+    return $this->response
+      ->withType('application/json')
+      ->withStatus(HttpStatusCodesEnum::HTTP_INTERNAL_SERVER_ERROR)
+      ->withStringBody(json_encode(['status' => 'failed']));
+  }
+
   /**
    * Dispatch an Enrollment Flow Step.
    * 
diff --git a/app/plugins/CoreEnroller/src/Model/Table/EmailVerifiersTable.php b/app/plugins/CoreEnroller/src/Model/Table/EmailVerifiersTable.php
index c515f1f0..4f64ed4e 100644
--- a/app/plugins/CoreEnroller/src/Model/Table/EmailVerifiersTable.php
+++ b/app/plugins/CoreEnroller/src/Model/Table/EmailVerifiersTable.php
@@ -85,7 +85,7 @@ public function initialize(array $config): void {
 
     $this->setPrimaryLink('enrollment_flow_step_id');
     $this->setRequiresCO(true);
-    $this->setAllowLookupPrimaryLink(['dispatch', 'display']);
+    $this->setAllowLookupPrimaryLink(['dispatch', 'display', 'resend']);
     
     // All the tabs share the same configuration in the ModelTable file
     $this->setTabsConfig(
@@ -112,6 +112,14 @@ public function initialize(array $config): void {
         'type' => 'select',
         'model' => 'MessageTemplates',
         'where' => ['context' => \App\Lib\Enum\MessageTemplateContextEnum::Verification]
+      ],
+      'cosettings' => [
+        'type' => 'auxiliary',
+        'model' => 'CoSettings'
+      ],
+      'types' => [
+        'type' => 'auxiliary',
+        'model' => 'Types'
       ]
     ]);
 
@@ -122,6 +130,7 @@ public function initialize(array $config): void {
         'dispatch' => true,
         'display' =>  true,
         'edit' =>     ['platformAdmin', 'coAdmin'],
+        'resend' =>  true,
         'view' =>     ['platformAdmin', 'coAdmin']
       ],
       // Actions that operate over a table (ie: do not require an $id)
@@ -412,8 +421,10 @@ public function prepare(
   public function sendVerificationRequest(
     EmailVerifier $emailVerifier, 
     Petition      $petition,
-    string        $mail
-  ) {
+    string        $mail,
+    bool          $resend = false,
+  ): bool
+  {
     // First check if there is already an existing Petition Verification.
     // If so, use that to get the existing Verification.
 
@@ -427,21 +438,7 @@ public function sendVerificationRequest(
                                            ])
                                            ->first();
 
-    if(!empty($pVerification)) {
-      // Request a new code
-
-      $this->llog('debug', "Sending replacement verification code to $mail for Petition " . $petition->id);
-
-      $verificationId = $Verifications->requestCodeForPetition(
-        $petition->id,
-        $mail,
-        $emailVerifier->message_template_id,
-        $emailVerifier->request_validity,
-        $pVerification->verification_id
-      );
-
-      // There's nothing to update in the Petition Verification
-    } else {
+    if (empty($pVerification)) {
       // Request Verification and create an associated Petition Verification
 
       $this->llog('debug', "Sending verification code to $mail for Petition " . $petition->id);
@@ -458,8 +455,28 @@ public function sendVerificationRequest(
           'petition_id'     => $petition->id,
           'mail'            => $mail,
           'verification_id' => $verificationId
-      ]));
+        ]));
+      return true;
     }
+
+    if ($resend) {
+      // Request a new code
+
+      $this->llog('debug', "Sending replacement verification code to $mail for Petition " . $petition->id);
+
+      $verificationId = $Verifications->requestCodeForPetition(
+        $petition->id,
+        $mail,
+        $emailVerifier->message_template_id,
+        $emailVerifier->request_validity,
+        $pVerification->verification_id
+      );
+      // There's nothing to update in the Petition Verification
+
+      return true;
+    }
+
+    return false;
   }
 
   /**
diff --git a/app/plugins/CoreEnroller/templates/EmailVerifiers/dispatch.inc b/app/plugins/CoreEnroller/templates/EmailVerifiers/dispatch.inc
index 62b3b9c4..c7613261 100644
--- a/app/plugins/CoreEnroller/templates/EmailVerifiers/dispatch.inc
+++ b/app/plugins/CoreEnroller/templates/EmailVerifiers/dispatch.inc
@@ -27,121 +27,21 @@
 
 declare(strict_types = 1);
 
-use CoreEnroller\Lib\Enum\VerificationModeEnum;
-use App\Lib\Util\StringUtilities;
-
 print $this->element('flash', []);
 
 // This view is intended to work with dispatch
-if($vv_action == 'dispatch') {
-  if($vv_op == 'index') {
-    // Render the list of known email addresses and their verification statuses.
-    // The configuration drives how many email addresses are required to complete this step.
-
-    print '<p>';
-
-    if($vv_all_done) {
-      print __d('core_enroller', 'information.EmailVerifiers.done');
-    } else {
-      switch($vv_config->mode) {
-        case VerificationModeEnum::All:
-          print __d('core_enroller', 'information.EmailVerifiers.A');
-          break;
-        case VerificationModeEnum::None:
-          print __d('core_enroller', 'information.EmailVerifiers.0');
-          break;
-        case VerificationModeEnum::One:
-          if($vv_minimum_met) {
-            print __d('core_enroller', 'information.EmailVerifiers.1.met');
-          } else {
-            print __d('core_enroller', 'information.EmailVerifiers.1.none');
-          }
-          break;
-      }
-    }
-
-    print '</p>';
-
-    print '
-    <table id="verifications-table" class="index-table list-mode">
-      <thead>
-        <tr>
-          <th>' . __d('controller', 'EmailAddresses', [1]) . '</th>
-          <th>' . __d('field', 'status') . '</th>
-        </tr>
-      </thead>
-      </tbody>
-    ';
-
-    foreach(array_keys($vv_email_addresses) as $addr) {
-      $verified = isset($vv_verified_addresses[$addr]) && $vv_verified_addresses[$addr];
-
-      $button = "";
-
-      if(!$verified) {
-        // We're already in a form here, so we need to use a GET URL to not mess things up.
-        // This also means we need to manually insert the token and petition ID, which is
-        // a bit duplicative with templates/Standard/dispatch.php
-
-        $url = [
-          'plugin'      => 'CoreEnroller',
-          'controller'  => 'email_verifiers',
-          'action'      => 'dispatch',
-          $vv_config->id,
-          '?' => [
-            'op'          => 'verify',
-            'petition_id' => $vv_petition->id,
-            // We base64 encode the address partly to not have bare email addresses in URLs
-            // and partly to avoid special characters (like dots) messing up the URL
-            'm'           => StringUtilities::urlbase64encode($addr)
-          ]
-        ];
-
-        if(isset($vv_token_ok) && $vv_token_ok && !empty($vv_petition->token)) {
-          $url['?']['token'] = $vv_petition->token;
-        }
-
-        $button = $this->Html->link(__d('operation', 'verify'), $url);
-      }
-
-      print '
-        <tr>
-          <td>' . $addr . '</td>
-          <td>' . __d('result', ($verified ? 'verified' : 'verified.not')) . $button . '</td>
-        </tr>
-      ';
-    }
-
-    print '
-      </tbody>
-    </table>
-    ';
-
-    if($vv_minimum_met) {
-      $this->Field->enableFormEditMode();
-
-      print $this->Form->hidden('op', ['default' => 'finish']);
-    }
-  } elseif($vv_op == 'verify') {
-    if(!empty($vv_verify_address)) {
-      // Render a form prompting for the code that was sent to the Enrollee
-
-      print __d('core_enroller', 'information.EmailVerifiers.code_sent', [$vv_verify_address]);
-
-      $this->Field->enableFormEditMode();
-
-      print $this->Form->hidden('op', ['default' => 'verify']);
-      print $this->Form->hidden('co_id', ['default' => $vv_cur_co->id]);
-      print $this->Form->hidden('m', ['default' => StringUtilities::urlbase64encode($vv_verify_address)]);
-
-      print $this->element('form/listItem', [
-        'arguments' => [
-          'fieldName' => 'code',
-          'fieldLabel' => "Code", //__d('field', 'mail')
-          'fieldOptions' => [
-            'required' => true
-          ]
-      ]]);
-    }
-  }
-}
\ No newline at end of file
+if ($vv_action !== 'dispatch') {
+  return;
+}
+
+if ($vv_op == 'index') {
+  $this->set('vv_include_cancel', false);
+  $this->set('vv_submit_button_label', __d('operation', 'finish'));
+  print $this->element('CoreEnroller.emailVerifiers/list');
+} elseif ($vv_op == 'verify') {
+  $this->set('vv_submit_button_label', __d('core_enroller', 'op.EmailVerifiers.verify'));
+  $this->set('vv_include_cancel', true);
+  print $this->element('CoreEnroller.emailVerifiers/verify');
+} else {
+  print __d('error', 'something.went.wrong');
+}
diff --git a/app/plugins/CoreEnroller/templates/element/emailVerifiers/list.php b/app/plugins/CoreEnroller/templates/element/emailVerifiers/list.php
new file mode 100644
index 00000000..b438d356
--- /dev/null
+++ b/app/plugins/CoreEnroller/templates/element/emailVerifiers/list.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * COmanage Registry Email Verifiers Petition Fields
+ *
+ * Portions licensed to the University Corporation for Advanced Internet
+ * Development, Inc. ("UCAID") under one or more contributor license agreements.
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @link          https://www.internet2.edu/comanage COmanage Project
+ * @package       registry
+ * @since         COmanage Registry v5.1.0
+ * @license       Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ */
+
+declare(strict_types = 1);
+
+use CoreEnroller\Lib\Enum\VerificationModeEnum;
+use App\Lib\Util\StringUtilities;
+
+// Render the list of known email addresses and their verification statuses.
+// The configuration drives how many email addresses are required to complete this step.
+
+$title = '';
+if($vv_all_done) {
+  $title = __d('core_enroller', 'information.EmailVerifiers.done');
+} else {
+  $title = match ($vv_config->mode) {
+    VerificationModeEnum::All  => __d('core_enroller', 'information.EmailVerifiers.A'),
+    VerificationModeEnum::None => __d('core_enroller', 'information.EmailVerifiers.0'),
+    VerificationModeEnum::One  => $vv_minimum_met
+      ? __d('core_enroller', 'information.EmailVerifiers.1.met')
+      : __d('core_enroller', 'information.EmailVerifiers.1.none'),
+    default => 'Unknown Verification Mode' // Optional fallback for unexpected cases
+  };
+
+}
+
+?>
+<p><?= $title ?></p>
+<table id="verifications-table" class="index-table list-mode">
+  <thead>
+  <tr>
+    <th><?= __d('controller', 'EmailAddresses', [1]) ?></th>
+    <th><?= __d('field', 'status') ?></th>
+  </tr>
+  </thead>
+  </tbody>
+
+  <?php foreach(array_keys($vv_email_addresses) as $addr): ?>
+  <?php
+  $verified = isset($vv_verified_addresses[$addr]) && $vv_verified_addresses[$addr];
+
+  $button = "";
+
+  if(!$verified) {
+    // We're already in a form here, so we need to use a GET URL to not mess things up.
+    // This also means we need to manually insert the token and petition ID, which is
+    // a bit duplicative with templates/Standard/dispatch.php
+
+    $url = [
+      'plugin'      => 'CoreEnroller',
+      'controller'  => 'email_verifiers',
+      'action'      => 'dispatch',
+      $vv_config->id,
+      '?' => [
+        'op'          => 'verify',
+        'petition_id' => $vv_petition->id,
+        // We base64 encode the address partly to not have bare email addresses in URLs
+        // and partly to avoid special characters (like dots) messing up the URL
+        'm'           => StringUtilities::urlbase64encode($addr)
+      ]
+    ];
+
+    if(isset($vv_token_ok) && $vv_token_ok && !empty($vv_petition->token)) {
+      $url['?']['token'] = $vv_petition->token;
+    }
+
+    $materialIcon = '<em class="material-symbols" aria-hidden="true">check</em>';
+    $button = $this->Html->link(
+      $materialIcon . ' ' . __d('operation', 'verify'),
+      $url,
+      [
+        'class' => 'btn btn-sm btn-tertiary float-end',
+        'escape' => false,
+      ]
+    );
+  }
+  ?>
+
+  <tr>
+    <td><?= $addr ?></td>
+    <td>
+    <?php if($verified): ?>
+      <?= __d('result', 'verified') ?>
+    <?php else: ?>
+      <span class="mr-1 badge bg-warning unverified"><?= __d('field', 'unverified')?></span>
+      <?= $button; ?>
+    <?php endif; ?>
+    </td>
+  </tr>
+  </tbody>
+</table>
+
+<?php
+
+if($vv_minimum_met || count($vv_email_addresses) === 0) {
+  $this->Field->enableFormEditMode();
+
+  print $this->Form->hidden('op', ['default' => 'finish']);
+
+  print $this->element('form/submit', ['label' => $vv_submit_button_label]);
+}
+?>
+<?php endforeach; ?>
diff --git a/app/plugins/CoreEnroller/templates/element/emailVerifiers/resendLinkSpa.php b/app/plugins/CoreEnroller/templates/element/emailVerifiers/resendLinkSpa.php
new file mode 100644
index 00000000..cfed64fe
--- /dev/null
+++ b/app/plugins/CoreEnroller/templates/element/emailVerifiers/resendLinkSpa.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ * COmanage Registry Resend Confirmation Link Vue.js component
+ *
+ * Portions licensed to the University Corporation for Advanced Internet
+ * Development, Inc. ("UCAID") under one or more contributor license agreements.
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @link          https://www.internet2.edu/comanage COmanage Project
+ * @package       registry
+ * @since         COmanage Registry v5.1.0
+ * @license       Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ */
+
+
+declare(strict_types = 1);
+
+/*
+ * Required:
+ * - $htmlId
+ * - $containerClasses
+ * - $vv_config
+ * - $m (encoded email)
+ * - $petitionId
+ * */
+
+// Load my helper functions
+$vueHelper = $this->loadHelper('Vue');
+
+$relativeUrl = "core-enroller/email-verifiers/resend/$vv_config->id"
+  . '?'
+  . "petition_id=$petitionId"
+  . "&m=$emailAddress"
+
+?>
+
+<script type="module">
+  import MiniLoader from "<?= $this->Url->script('comanage/components/common/mini-loader.js')?>?time=<?= time() ?>";
+  
+  const app = Vue.createApp({
+    data() {
+      return {
+        error: '',
+        message: '', // Message to show (success or error)
+        loading: false, // Loading state for the fetch request
+        controller: null, // AbortController instance,
+        txt: JSON.parse('<?= json_encode($vueHelper->locales()) ?>'),
+        app: {
+          coId: <?= $vv_cur_co->id ?>,
+          types: <?= json_encode($types) ?>,
+          cosettings: <?= json_encode($cosettings) ?>
+        },
+        api: {
+          webroot: '<?= $this->request->getAttribute('webroot') ?>',
+          // co_id query parameter is required since it is the People's primary link
+          resendCode: `<?= $this->request->getAttribute('webroot') . $relativeUrl ?>`
+        },
+        txtPlugin: {
+          'resendPreText': "<?= __d('core_enroller','information.EmailVerifiers.resend-pre-text') ?>",
+          'resend': '<?= __d('core_enroller','information.EmailVerifiers.resend') ?>',
+          'abort': '<?= __d('core_enroller','information.EmailVerifiers.abort') ?>',
+          'sending': '<?= __d('core_enroller','information.EmailVerifiers.sending') ?>',
+          'success': '<?= __d('core_enroller','information.EmailVerifiers.success') ?>',
+        }
+      }
+    },
+    components: {
+      MiniLoader
+    },
+    methods: {
+      async fetchData() {
+        this.message = ""; // Clear any previous messages/errors
+        this.error = ''; 
+        this.loading = true; // Set loading state to true
+
+        // Create a new AbortController instance
+        this.controller = new AbortController();
+
+        const apiUrl = this.api.resendCode; // Replace this URL with your API endpoint
+
+        let request_init = {
+          headers: new Headers({
+            'X-Requested-With': 'XMLHttpRequest',
+            'Accept': 'application/json',
+          }),
+          method: 'GET',
+          signal: this.controller.signal,
+        }
+        // AJAX Request
+        let requestObj = new Request(apiUrl, request_init);
+        
+        try {
+          // Perform the fetch request with signal
+          const response = await fetch(requestObj);
+
+          // Check if the response is successful
+          if (!response.ok) {
+            throw new Error(`Error: ${response.status} - ${response.statusText}`);
+          }
+          
+          const data = await response.json();
+          this.message = this.txtPlugin.success;
+        } catch (error) {
+          // Handle fetch errors
+          if (error.name === "AbortError") {
+            // If fetch was aborted
+            this.error = "Request aborted.";
+          } else {
+            this.error = `Request Failed: ${error.message}`;
+          }
+        } finally {
+          this.loading = false; // Reset the loading state
+        }
+      },
+      abortRequest() {
+        // Abort the ongoing fetch request, if any
+        if (this.controller) {
+          this.controller.abort();
+          this.controller = null;
+        }
+      },
+    },
+    computed: {
+      getMiniLoaderClasses: function() {
+        return "co-loading-mini-container d-inline ms-1"
+      },
+      getAlertClasses: function() {
+        if (this.error) {
+          return "alert alert-danger alert-dismissible co-alert"
+        } else if (this.message) {
+          return "alert alert-success alert-dismissible co-alert"
+        } else {
+          return "alert alert-info alert-dismissible co-alert"
+        }
+      }
+    },
+    template: `
+        <!-- Display Success or Error Message -->
+        <div v-if="message || error"  class="alert-container mb-3" id="flash-messages">
+            <div :class="getAlertClasses" role="alert">
+                <div class="alert-body d-flex align-items-center">
+                    <span class="alert-title d-flex align-items-center">
+                        <span v-if="error" class="material-symbols-outlined alert-icon">report_problem</span>
+                        <span v-if="message" class="material-symbols-outlined alert-icon">check_circle</span>
+                    </span>
+                    <span v-if="error" class="alert-message">{{ error }}</span>
+                    <span  v-if="message" class="alert-message">{{ message }}</span>
+                    <span class="alert-button">
+                        <button type="button" class="btn-close nospin" data-bs-dismiss="alert" aria-label="Close"></button>
+                    </span>
+                </div>
+            </div>
+        </div>
+        <div v-if="!loading">
+            <span class="me-1">{{ txtPlugin.resendPreText }}</span>
+            <a href="#" class="spin" @click="fetchData">{{ txtPlugin.resend }}</a>
+        </div>
+        <div v-if="loading">
+            <span class="me-1">{{ txtPlugin.sending }}</span>
+            <MiniLoader :isLoading="loading" :classes="getMiniLoaderClasses"/>
+        </div>
+    `
+  });
+
+
+  app.use(primevue.config.default, {unstyled: true});
+
+  // Mount the component and provide a global reference for this app instance.
+  window.<?= str_replace('-', '', $htmlId) ?> = app.mount("#<?= $htmlId ?>-container");
+</script>
+
+<div id="<?= $htmlId ?>-container"  class="<?= $containerClasses ?>"></div>
diff --git a/app/plugins/CoreEnroller/templates/element/emailVerifiers/verify.php b/app/plugins/CoreEnroller/templates/element/emailVerifiers/verify.php
new file mode 100644
index 00000000..8dcc7a4c
--- /dev/null
+++ b/app/plugins/CoreEnroller/templates/element/emailVerifiers/verify.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * COmanage Registry Verify Email
+ *
+ * Portions licensed to the University Corporation for Advanced Internet
+ * Development, Inc. ("UCAID") under one or more contributor license agreements.
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @link          https://www.internet2.edu/comanage COmanage Project
+ * @package       registry
+ * @since         COmanage Registry v5.1.0
+ * @license       Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ */
+
+declare(strict_types = 1);
+
+use App\Lib\Util\StringUtilities;
+
+if(empty($vv_verify_address)) {
+  print __d('core_enroller', 'information.EmailVerifiers.done');
+  return;
+}
+
+// Render a form prompting for the code that was sent to the Enrollee
+
+print __d('core_enroller', 'information.EmailVerifiers.code_sent', [$vv_verify_address]);
+
+$this->Field->enableFormEditMode();
+
+$m = StringUtilities::urlbase64encode($vv_verify_address);
+
+print $this->Form->hidden('op', ['default' => 'verify']);
+print $this->Form->hidden('co_id', ['default' => $vv_cur_co->id]);
+print $this->Form->hidden('m', ['default' => $m]);
+
+print $this->element('form/listItem', [
+  'arguments' => [
+    'fieldName' => 'code',
+    'fieldLabel' => "Code", //__d('field', 'mail')
+    'fieldOptions' => [
+      'required' => true
+    ]
+  ]]);
+
+$resendLink = $this->Html->link(
+  __d('core_enroller', 'Resend'),
+  ['controller' => 'email_verifiers', 'action' => 'resend', $vv_verify_address],
+  ['class' => 'text-primary']
+);
+
+?>
+<?php if($this->Field->isEditable()): ?>
+  <li class="fields-submit">
+    <div class="field">
+      <div class="field-name">
+        <span class="required">* <?= __d('field', 'required') ?></span>
+      </div>
+      <div class="field-info">
+        <?= $this->Form->submit($vv_submit_button_label) ?>
+        <?php if(!empty($vv_include_cancel)): ?>
+          <button type="button" onclick="history.back()" class="btn btn-cancel">
+            <?= __d('operation','cancel') ?>
+          </button>
+        <?php endif; ?>
+      </div>
+    </div>
+  </li>
+<?php endif; ?>
+
+<!-- Resend Link - SPA module -->
+<?= $this->element('CoreEnroller.emailVerifiers/resendLinkSpa', [
+  'htmlId' => 'resend-link',
+  'petitionId' => $vv_petition->id,
+  'containerClasses' => 'border-top border-1 pt-2 text-center text-muted',
+  'emailAddress' => $m,
+  'vv_config' => $vv_config,
+]) ?>
+
diff --git a/app/resources/locales/en_US/error.po b/app/resources/locales/en_US/error.po
index dc8c648e..7f0408c5 100644
--- a/app/resources/locales/en_US/error.po
+++ b/app/resources/locales/en_US/error.po
@@ -214,6 +214,9 @@ msgstr "{0} must be provided"
 msgid "invalid"
 msgstr "Invalid value \"{0}\""
 
+msgid "invalid.request"
+msgstr "Invalid Request"
+
 msgid "javascript.copy"
 msgstr "Could not copy."
 
@@ -364,6 +367,9 @@ msgstr "No type defined for table \"{0}\" column \"{1}\""
 msgid "schema.parse"
 msgstr "Failed to parse file {0}"
 
+msgid "something.went.wrong"
+msgstr "Ooops... Something went wrong."
+
 msgid "setup.co.comanage"
 msgstr "Failed to setup COmanage CO"
 
diff --git a/app/resources/locales/en_US/operation.po b/app/resources/locales/en_US/operation.po
index 7a5c2ff2..908a83da 100644
--- a/app/resources/locales/en_US/operation.po
+++ b/app/resources/locales/en_US/operation.po
@@ -180,6 +180,9 @@ msgstr "Sync Record to CO"
 msgid "filter"
 msgstr "Filter"
 
+msgid "finish"
+msgstr "Finish"
+
 msgid "first"
 msgstr "First"
 
diff --git a/app/src/Command/TransmogrifyCommand.php b/app/src/Command/TransmogrifyCommand.php
index 37871a6b..93a72c00 100644
--- a/app/src/Command/TransmogrifyCommand.php
+++ b/app/src/Command/TransmogrifyCommand.php
@@ -620,7 +620,7 @@ public function execute(Arguments $args, ConsoleIo $io) {
       $err = 0;
       
       // Loop over each row from the inbound table.
-      while($row = $stmt->fetch()) {
+      while($row = $stmt->fetchAssociative()) {
         if(!empty($row[ $this->tables[$t]['displayField'] ])) {
           $io->verbose("$t " . $row[ $this->tables[$t]['displayField'] ]);
         }
diff --git a/app/src/Lib/Enum/HttpStatusCodesEnum.php b/app/src/Lib/Enum/HttpStatusCodesEnum.php
new file mode 100644
index 00000000..7a6617fc
--- /dev/null
+++ b/app/src/Lib/Enum/HttpStatusCodesEnum.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * COmanage Registry HTTP Statis Codes Enum
+ *
+ * Portions licensed to the University Corporation for Advanced Internet
+ * Development, Inc. ("UCAID") under one or more contributor license agreements.
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @link          https://www.internet2.edu/comanage COmanage Project
+ * @package       registry
+ * @since         COmanage Registry v5.1.0
+ * @license       Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+ */
+
+declare(strict_types = 1);
+
+namespace App\Lib\Enum;
+
+// XXX [REF]https://httpstatuses.com
+class HttpStatusCodesEnum extends StandardEnum
+{
+  // [Informational 1xx]
+  const HTTP_CONTINUE                        = 100;
+  const HTTP_SWITCHING_PROTOCOLS             = 101;
+
+  // [Successful 2xx]
+  const HTTP_OK                              = 200;
+  const HTTP_CREATED                         = 201;
+  const HTTP_ACCEPTED                        = 202;
+  const HTTP_NONAUTHORITATIVE_INFORMATION    = 203;
+  const HTTP_NO_CONTENT                      = 204;
+  const HTTP_RESET_CONTENT                   = 205;
+  const HTTP_PARTIAL_CONTENT                 = 206;
+
+  // [Redirection 3xx]
+  const HTTP_MULTIPLE_CHOICES                = 300;
+  const HTTP_MOVED_PERMANENTLY               = 301;
+  const HTTP_FOUND                           = 302;
+  const HTTP_SEE_OTHER                       = 303;
+  const HTTP_NOT_MODIFIED                    = 304;
+  const HTTP_USE_PROXY                       = 305;
+  const HTTP_UNUSED                          = 306;
+  const HTTP_TEMPORARY_REDIRECT              = 307;
+
+  // [Client Error 4xx]
+  const errorCodesBeginAt                    = 400;
+  const HTTP_BAD_REQUEST                     = 400;
+  const HTTP_UNAUTHORIZED                    = 401;
+  const HTTP_PAYMENT_REQUIRED                = 402;
+  const HTTP_FORBIDDEN                       = 403;
+  const HTTP_NOT_FOUND                       = 404;
+  const HTTP_METHOD_NOT_ALLOWED              = 405;
+  const HTTP_NOT_ACCEPTABLE                  = 406;
+  const HTTP_PROXY_AUTHENTICATION_REQUIRED   = 407;
+  const HTTP_REQUEST_TIMEOUT                 = 408;
+  const HTTP_CONFLICT                        = 409;
+  const HTTP_GONE                            = 410;
+  const HTTP_LENGTH_REQUIRED                 = 411;
+  const HTTP_PRECONDITION_FAILED             = 412;
+  const HTTP_REQUEST_ENTITY_TOO_LARGE        = 413;
+  const HTTP_REQUEST_URI_TOO_LONG            = 414;
+  const HTTP_UNSUPPORTED_MEDIA_TYPE          = 415;
+  const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+  const HTTP_EXPECTATION_FAILED              = 417;
+
+  // [Server Error 5xx]
+  const HTTP_INTERNAL_SERVER_ERROR           = 500;
+  const HTTP_NOT_IMPLEMENTED                 = 501;
+  const HTTP_BAD_GATEWAY                     = 502;
+  const HTTP_SERVICE_UNAVAILABLE             = 503;
+  const HTTP_GATEWAY_TIMEOUT                 = 504;
+  const HTTP_VERSION_NOT_SUPPORTED           = 505;
+}
\ No newline at end of file
diff --git a/app/templates/Standard/dispatch.php b/app/templates/Standard/dispatch.php
index be78db43..c65fc128 100644
--- a/app/templates/Standard/dispatch.php
+++ b/app/templates/Standard/dispatch.php
@@ -31,6 +31,10 @@
 $modelsName = $this->name;
 // $tablename = models
 $tableName = \Cake\Utility\Inflector::tableize(\Cake\Utility\Inflector::singularize($this->name));
+// Populate the AutoViewVars. These are the same we do for the EnrollmentAttributes configuration view
+$this->Petition->populateAutoViewVars();
+// We just populated the AutoViewVars. Add them to the current context
+extract($this->viewVars);
 
 // $vv_template_path will be set for plugins
 $templatePath = $vv_template_path ?? ROOT . DS . 'templates' . DS . $modelsName;
@@ -76,7 +80,9 @@
 // Set the Include file name
 // Will be used by the unorderedList element below
 $this->set('vv_fields_inc', 'dispatch.inc');
-$this->set('vv_submit_button_label', __d('operation', 'continue'));
+if (empty($vv_submit_button_label)) {
+  $this->set('vv_submit_button_label', __d('operation', 'continue'));
+}
 
 // By default, the form will POST to the current controller
 // Note we need to open the form for view so Cake will autopopulate values