diff --git a/modules/recurring_events_registration/recurring_events_registration.api.php b/modules/recurring_events_registration/recurring_events_registration.api.php
new file mode 100644
index 0000000000000000000000000000000000000000..c9be65141eee84adfa981222215ddbdedccc3cbd
--- /dev/null
+++ b/modules/recurring_events_registration/recurring_events_registration.api.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Custom hooks exposed by the recurring_events_registration module.
+ */
+
+use Drupal\recurring_events_registration\Entity\Registrant;
+
+/**
+ * Alter the registrant to be promoted from the waitlist.
+ *
+ * If you need to apply custom logic to determining which user should be
+ * promoted from the waitlist when a registration spot opens up you can
+ * implement this hook and write your custom logic here. The hook must return an
+ * instance of Drupal\recurring_events_registration\Entity\Registrant for the
+ * specified event, which can be retrieved from the registrant entity.
+ *
+ * @param Drupal\recurring_events_registration\Entity\Registrant $registrant
+ *   The default selected registrant.
+ *
+ * @return Drupal\recurring_events_registration\Entity\Registrant
+ *   A valid registrant entity.
+ */
+function hook_recurring_events_registration_first_waitlist_alter(Registrant $registrant) {
+  // Find the ID of the registrant you wish to promote, then load the entity.
+  $new_registrant = \Drupal::entityTypeManager()->getStorage('registrant')->load($id);
+  return $new_registrant;
+}
diff --git a/modules/recurring_events_registration/recurring_events_registration.module b/modules/recurring_events_registration/recurring_events_registration.module
index d434a759cf71c68fbf6a14568bcfd0c07de116a5..dbbcafd6fc7e0635ad30aed2df9a49aba0578bb2 100644
--- a/modules/recurring_events_registration/recurring_events_registration.module
+++ b/modules/recurring_events_registration/recurring_events_registration.module
@@ -9,6 +9,11 @@ use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\recurring_events_registration\Entity\Registrant;
+use Drupal\recurring_events\Entity\EventSeries;
+use Drupal\recurring_events\Entity\EventInstance;
+use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Implements hook_help().
@@ -108,3 +113,120 @@ function recurring_events_registration_mail($key, &$message, $params) {
   }
   return FALSE;
 }
+
+/**
+ * Implements hook_recurring_events_save_pre_instances_deletion().
+ */
+function recurring_events_registration_recurring_events_save_pre_instances_deletion(EventSeries $event_series, FormStateInterface $form_state) {
+  $registration_creation_service = \Drupal::service('recurring_events_registration.creation_service');
+  $registration_creation_service->setEventSeries($event_series);
+
+  // Get all the registrants who have registered for any event in this series.
+  $registrants = $registration_creation_service->retrieveAllSeriesRegisteredParties();
+  if (empty($registrants)) {
+    return;
+  }
+
+  $key = 'modification_notification';
+
+  // Send an email to all registrants.
+  foreach ($registrants as $registrant) {
+    recurring_events_registration_send_notification($key, $registrant);
+  }
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function recurring_events_registration_entity_update(EntityInterface $entity) {
+  if ($entity->getEntityTypeId() === 'eventinstance') {
+    $date_changes = FALSE;
+    $original = $entity->original;
+
+    $date_changes = !(serialize($entity->date->getValue()) === serialize($original->date->getValue()));
+    if (!$date_changes) {
+      return;
+    }
+
+    $registration_creation_service = \Drupal::service('recurring_events_registration.creation_service');
+    $registration_creation_service->setEventInstance($entity);
+
+    $registrants = $registration_creation_service->retrieveRegisteredParties();
+    if (empty($registrants)) {
+      return;
+    }
+
+    $key = 'modification_notification';
+
+    // Send an email to all registrants.
+    foreach ($registrants as $registrant) {
+      recurring_events_registration_send_notification($key, $registrant);
+    }
+  }
+}
+
+/**
+ * Implements hook_recurring_events_pre_delete_instance().
+ */
+function recurring_events_registration_recurring_events_pre_delete_instance(EventInstance $instance) {
+  $registration_creation_service = \Drupal::service('recurring_events_registration.creation_service');
+  $registration_creation_service->setEventInstance($instance);
+
+  $registrants = $registration_creation_service->retrieveRegisteredParties();
+  if (empty($registrants)) {
+    return;
+  }
+
+  $key = 'cancellation_notification';
+
+  // Send an email to all registrants.
+  foreach ($registrants as $registrant) {
+    recurring_events_registration_send_notification($key, $registrant);
+    $registrant->delete();
+  }
+}
+
+/**
+ * Implements hook_recurring_events_pre_delete_instances().
+ */
+function recurring_events_registration_recurring_events_pre_delete_instances(EventSeries $event_series) {
+  $registration_creation_service = \Drupal::service('recurring_events_registration.creation_service');
+  $registration_creation_service->setEventSeries($event_series);
+
+  // Get all the registrants who have registered for any event in this series.
+  $registrants = $registration_creation_service->retrieveAllSeriesRegisteredParties();
+  if (empty($registrants)) {
+    return;
+  }
+
+  $key = 'cancellation_notification';
+
+  // Send an email to all registrants.
+  foreach ($registrants as $registrant) {
+    recurring_events_registration_send_notification($key, $registrant);
+    $registrant->delete();
+  }
+}
+
+/**
+ * Send a notification message.
+ *
+ * @param string $key
+ *   The mail key used to determine the message and subject.
+ * @param \Drupal\recurring_events_registration\Entity\Registrant $registrant
+ *   The registrant this email relates to.
+ */
+function recurring_events_registration_send_notification($key, Registrant $registrant) {
+  $config = \Drupal::config('recurring_events_registration.registrant.config');
+  $send_email = $config->get('email_notifications');
+  if ($send_email) {
+    $params = [
+      'registrant' => $registrant,
+    ];
+
+    $to = $registrant->email->value;
+
+    $mail = \Drupal::service('plugin.manager.mail');
+    $mail->mail('recurring_events_registration', $key, $to, \Drupal::languageManager()->getDefaultLanguage()->getId(), $params);
+  }
+}
diff --git a/modules/recurring_events_registration/recurring_events_registration.tokens.inc b/modules/recurring_events_registration/recurring_events_registration.tokens.inc
index e952461af5b55763a8e61eb9d7096c9edc42d122..3f1d1ecf6d85456f63c2560730cd8a2dac61967b 100644
--- a/modules/recurring_events_registration/recurring_events_registration.tokens.inc
+++ b/modules/recurring_events_registration/recurring_events_registration.tokens.inc
@@ -48,7 +48,7 @@ function recurring_events_registration_tokens($type, $tokens, array $data, array
     $event_instance = $data['eventinstance'];
     $event_series = $event_instance->getEventSeries();
     $creation_service = \Drupal::service('recurring_events_registration.creation_service');
-    $creation_service->setEvents($event_instance);
+    $creation_service->setEventInstance($event_instance);
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'reg_url':
diff --git a/modules/recurring_events_registration/src/Controller/RegistrantController.php b/modules/recurring_events_registration/src/Controller/RegistrantController.php
index c4da634c610d753107d5be38a0ebe967409ea249..6ae3bb537f389bfdf356f1f419116039902f5bff 100644
--- a/modules/recurring_events_registration/src/Controller/RegistrantController.php
+++ b/modules/recurring_events_registration/src/Controller/RegistrantController.php
@@ -53,7 +53,7 @@ class RegistrantController extends ControllerBase implements ContainerInjectionI
   public static function hasRegistration(EventInstance $eventinstance) {
     if (!empty($eventinstance)) {
       $service = \Drupal::service('recurring_events_registration.creation_service');
-      $service->setEvents($eventinstance);
+      $service->setEventInstance($eventinstance);
       if ($service->hasRegistration()) {
         return AccessResult::allowed();
       }
diff --git a/modules/recurring_events_registration/src/Entity/Registrant.php b/modules/recurring_events_registration/src/Entity/Registrant.php
index 25e0ae2e2bba9911424134070d36331aa68c4fc3..49617a93060b8a44c92501908dd5714bf2ce9f0a 100644
--- a/modules/recurring_events_registration/src/Entity/Registrant.php
+++ b/modules/recurring_events_registration/src/Entity/Registrant.php
@@ -71,23 +71,8 @@ class Registrant extends ContentEntityBase implements RegistrantInterface {
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
     if (!$update) {
-      $config = \Drupal::config('recurring_events_registration.registrant.config');
-      $send_email = $config->get('email_notifications');
-      if ($send_email) {
-        $params = [
-          'registrant' => $this,
-        ];
-
-        $to = $this->email->value;
-
-        $key = 'registration_notification';
-
-        if ($this->getWaitlist()) {
-          $key = 'waitlist_notification';
-        }
-        $mail = \Drupal::service('plugin.manager.mail');
-        $mail->mail('recurring_events_registration', $key, $to, \Drupal::languageManager()->getDefaultLanguage()->getId(), $params);
-      }
+      $key = 'registration_notification';
+      recurring_events_registration_send_notification($key, $this);
     }
   }
 
diff --git a/modules/recurring_events_registration/src/Form/RegistrantDeleteForm.php b/modules/recurring_events_registration/src/Form/RegistrantDeleteForm.php
index 33a80fbd8b5bf528792e771efc50bb7d1171f904..1cf64a5c6c1d5807cd476751ab69621b7d8b15ea 100644
--- a/modules/recurring_events_registration/src/Form/RegistrantDeleteForm.php
+++ b/modules/recurring_events_registration/src/Form/RegistrantDeleteForm.php
@@ -84,7 +84,7 @@ class RegistrantDeleteForm extends ContentEntityDeleteForm {
     $form_state->setRedirectUrl($eventinstance->toUrl('canonical'));
 
     $service = \Drupal::service('recurring_events_registration.creation_service');
-    $service->setEvents($eventinstance);
+    $service->setEventInstance($eventinstance);
     if ($service->hasWaitlist() && $entity->waitlist->value == '0') {
       $service->promoteFromWaitlist();
     }
diff --git a/modules/recurring_events_registration/src/Form/RegistrantForm.php b/modules/recurring_events_registration/src/Form/RegistrantForm.php
index 2d72240ce1f2bde47d8e466b11f81e7f0c303209..4be8a80771e4c6664377a5c1aa36ff9ecf58caee 100644
--- a/modules/recurring_events_registration/src/Form/RegistrantForm.php
+++ b/modules/recurring_events_registration/src/Form/RegistrantForm.php
@@ -152,7 +152,7 @@ class RegistrantForm extends ContentEntityForm {
     $form_state->setTemporaryValue('event', $event_instance);
 
     // Use the registration creation service to grab relevant data.
-    $this->creationService->setEvents($event_instance);
+    $this->creationService->setEventInstance($event_instance);
     $availability = $this->creationService->retrieveAvailability();
     $waitlist = $this->creationService->hasWaitlist();
     $registration_open = $this->creationService->registrationIsOpen();
@@ -340,7 +340,7 @@ class RegistrantForm extends ContentEntityForm {
       $event_instance = $form_state->getTemporaryValue('event');
 
       // Use the registration creation service to grab relevant data.
-      $this->creationService->setEvents($event_instance);
+      $this->creationService->setEventInstance($event_instance);
       // Just to be sure we have a fresh copy of the event series.
       $this->creationService->setEventSeries($event_series);
 
@@ -385,7 +385,7 @@ class RegistrantForm extends ContentEntityForm {
     $event_instance = $form_state->getTemporaryValue('event');
 
     // Use the registration creation service to grab relevant data.
-    $this->creationService->setEvents($event_instance);
+    $this->creationService->setEventInstance($event_instance);
     // Just to be sure we have a fresh copy of the event series.
     $this->creationService->setEventSeries($event_series);
 
diff --git a/modules/recurring_events_registration/src/Form/RegistrantSettingsForm.php b/modules/recurring_events_registration/src/Form/RegistrantSettingsForm.php
index b9421da011e57bd5a98d209fefe01adea2274e92..276707002b0054ac24dc776ffe77a134fa7a54d4 100644
--- a/modules/recurring_events_registration/src/Form/RegistrantSettingsForm.php
+++ b/modules/recurring_events_registration/src/Form/RegistrantSettingsForm.php
@@ -167,7 +167,7 @@ class RegistrantSettingsForm extends ConfigFormBase {
       '#type' => 'details',
       '#title' => $this->t('Registration Notification'),
       '#open' => TRUE,
-      '#description' => $this->t('Enable and configure registration notifications') . ' ' . $token_help,
+      '#description' => $this->t('Enable and configure registration notifications.') . ' ' . $token_help,
       '#group' => 'emails',
     ];
     $form['notifications']['registration']['registration_notification'] = [
@@ -203,7 +203,7 @@ class RegistrantSettingsForm extends ConfigFormBase {
     $form['notifications']['waitlist'] = [
       '#type' => 'details',
       '#title' => $this->t('Waitlist Notification'),
-      '#description' => $this->t('Enable and configure waitlist notifications') . ' ' . $token_help,
+      '#description' => $this->t('Enable and configure waitlist notifications.') . ' ' . $token_help,
       '#group' => 'emails',
     ];
     $form['notifications']['waitlist']['waitlist_notification'] = [
@@ -239,7 +239,7 @@ class RegistrantSettingsForm extends ConfigFormBase {
     $form['notifications']['promotion'] = [
       '#type' => 'details',
       '#title' => $this->t('Promotion Notification'),
-      '#description' => $this->t('Enable and configure promotion notifications') . ' ' . $token_help,
+      '#description' => $this->t('Enable and configure promotion notifications.') . ' ' . $token_help,
       '#group' => 'emails',
     ];
     $form['notifications']['promotion']['promotion_notification'] = [
@@ -275,7 +275,7 @@ class RegistrantSettingsForm extends ConfigFormBase {
     $form['notifications']['cancellation'] = [
       '#type' => 'details',
       '#title' => $this->t('Cancellation Notification'),
-      '#description' => $this->t('Enable and configure cancellation notifications') . ' ' . $token_help,
+      '#description' => $this->t('Enable and configure cancellation notifications.') . ' ' . $token_help,
       '#group' => 'emails',
     ];
     $form['notifications']['cancellation']['cancellation_notification'] = [
@@ -311,7 +311,7 @@ class RegistrantSettingsForm extends ConfigFormBase {
     $form['notifications']['modification'] = [
       '#type' => 'details',
       '#title' => $this->t('Modification Notification'),
-      '#description' => $this->t('Enable and configure modification notifications') . ' ' . $token_help,
+      '#description' => $this->t('Enable and configure modification notifications.') . ' ' . $token_help,
       '#group' => 'emails',
     ];
     $form['notifications']['modification']['modification_notification'] = [
diff --git a/modules/recurring_events_registration/src/RegistrantAccessControlHandler.php b/modules/recurring_events_registration/src/RegistrantAccessControlHandler.php
index 7dda869c5a47884a3f04448ccbe3ae3671ed9aba..b78c4cb0a3f845c79870c469f901c4653a316dfc 100644
--- a/modules/recurring_events_registration/src/RegistrantAccessControlHandler.php
+++ b/modules/recurring_events_registration/src/RegistrantAccessControlHandler.php
@@ -41,7 +41,7 @@ class RegistrantAccessControlHandler extends EntityAccessControlHandler {
     $params = \Drupal::request()->attributes->all();
     if (!empty($params['eventinstance'])) {
       $service = \Drupal::service('recurring_events_registration.creation_service');
-      $service->setEvents($params['eventinstance']);
+      $service->setEventInstance($params['eventinstance']);
       if ($service->hasRegistration()) {
         return AccessResult::allowedIfHasPermission($account, 'add registrant entities');
       }
diff --git a/modules/recurring_events_registration/src/RegistrationCreationService.php b/modules/recurring_events_registration/src/RegistrationCreationService.php
index 93fd0814f19e2a84d386b086781218ac56228fbf..ce1dd30c41dff12d137b569d60641ce1381fb287 100644
--- a/modules/recurring_events_registration/src/RegistrationCreationService.php
+++ b/modules/recurring_events_registration/src/RegistrationCreationService.php
@@ -107,7 +107,7 @@ class RegistrationCreationService {
    * @param Drupal\recurring_events\Entity\EventInstance $event_instance
    *   The event instance.
    */
-  public function setEvents(EventInstance $event_instance) {
+  public function setEventInstance(EventInstance $event_instance) {
     $this->eventInstance = $event_instance;
     $this->eventSeries = $event_instance->getEventSeries();
   }
@@ -133,7 +133,7 @@ class RegistrationCreationService {
    *   The user ID for whom to retrieve registrants.
    *
    * @return array
-   *   An array of email contacts.
+   *   An array of registrants.
    */
   public function retrieveRegisteredParties($include_nonwaitlisted = TRUE, $include_waitlisted = TRUE, $uid = FALSE) {
     $parties = [];
@@ -171,6 +171,26 @@ class RegistrationCreationService {
     return $parties;
   }
 
+  /**
+   * Retreive all registered parties for a series.
+   *
+   * @return array
+   *   An array of registrants.
+   */
+  public function retrieveAllSeriesRegisteredParties() {
+    $parties = [];
+    $properties = [
+      'eventseries_id' => $this->eventSeries->id(),
+    ];
+
+    $results = $this->storage->loadByProperties($properties);
+
+    if (!empty($results)) {
+      $parties = $results;
+    }
+    return $parties;
+  }
+
   /**
    * Get registration availability.
    *
@@ -468,21 +488,8 @@ class RegistrationCreationService {
         $first_waitlist->setWaitlist('0');
         $first_waitlist->save();
 
-        // Optionally send an email notification.
-        $config = \Drupal::config('recurring_events_registration.registrant.config');
-        $send_email = $config->get('email_notifications');
-        if ($send_email) {
-          $params = [
-            'registrant' => $first_waitlist,
-          ];
-
-          $to = $first_waitlist->email->value;
-
-          $key = 'promotion_notification';
-
-          $mail = \Drupal::service('plugin.manager.mail');
-          $mail->mail('recurring_events_registration', $key, $to, \Drupal::languageManager()->getDefaultLanguage()->getId(), $params);
-        }
+        $key = 'promotion_notification';
+        recurring_events_registration_send_notification($key, $first_waitlist);
       }
     }
   }
diff --git a/recurring_events.api.php b/recurring_events.api.php
index 36d285d2544f76e529649ce7a96af110875e2f1b..c13ab85355a84368bacf9dac467f8d358b4adfa3 100644
--- a/recurring_events.api.php
+++ b/recurring_events.api.php
@@ -59,3 +59,140 @@ function hook_recurring_events_event_instance_alter(array &$event_instance = [])
   // Change the series ID.
   $event_instance['event_series_id'] = 12;
 }
+
+/**
+ * Alter the form config array after it has been generated.
+ *
+ * @param array $form_config
+ *   An array of data representing the date recurring configuration.
+ */
+function hook_recurring_events_form_config_array_alter(array &$form_config = []) {
+  // Remove the first custom date.
+  unset($form_config['custom_dates'][0]);
+}
+
+/**
+ * Alter the entity config array after it has been generated.
+ *
+ * @param array $entity_config
+ *   An array of data representing the date recurring configuration.
+ */
+function hook_recurring_events_entity_config_array_alter(array &$entity_config = []) {
+  // Remove the first custom date.
+  unset($form_config['custom_dates'][0]);
+}
+
+/**
+ * Alter the diff array after it has been generated.
+ *
+ * @param array $diff
+ *   An array of differences between the stored and updated event date config.
+ */
+function hook_recurring_events_diff_array_alter(array &$diff = []) {
+  // Do not show differences in custom dates.
+  unset($form_config['custom_dates']);
+}
+
+/**
+ * Execute custom code before event instances are deleted.
+ *
+ * When an eventseries is updated and has date recurring configuration changes
+ * the EventCreationService will delete all the instances that existed before
+ * and recreate them. This hook allows you to execute code prior to the deletion
+ * of those instances.
+ *
+ * @param Drupal\recurring_events\Entity\EventSeries $event_series
+ *   The eventseries being altered.
+ * @param Drupal\Core\Form\FormStateInterface $form_state
+ *   The form state of an updated event series entity.
+ */
+function hook_recurring_events_save_pre_instances_deletion(EventSeries $event_series, FormStateInterface $form_state) {
+}
+
+/**
+ * Execute custom code after event instances are deleted.
+ *
+ * When an eventseries is updated and has date recurring configuration changes
+ * the EventCreationService will delete all the instances that existed before
+ * and recreate them. This hook allows you to execute code after the deletion
+ * of those instances.
+ *
+ * @param Drupal\recurring_events\Entity\EventSeries $event_series
+ *   The eventseries being altered.
+ * @param Drupal\Core\Form\FormStateInterface $form_state
+ *   The form state of an updated event series entity.
+ */
+function hook_recurring_events_save_post_instances_deletion(EventSeries $event_series, FormStateInterface $form_state) {
+}
+
+/**
+ * Execute custom code before a specific event instance is deleted.
+ *
+ * When an eventseries is updated and has date recurring configuration changes
+ * the EventCreationService will delete all the instances that existed before
+ * and recreate them. This hook allows you to execute code prior to the deletion
+ * of each instance.
+ *
+ * @param Drupal\recurring_events\Entity\EventSeries $event_series
+ *   The eventseries being altered.
+ * @param Drupal\recurring_events\Entity\EventInstance $event_instance
+ *   The event instance being deleted.
+ */
+function hook_recurring_events_save_pre_instance_deletion(EventSeries $event_series, EventInstance $event_instance) {
+}
+
+/**
+ * Execute custom code after a specific event instance is deleted.
+ *
+ * When an eventseries is updated and has date recurring configuration changes
+ * the EventCreationService will delete all the instances that existed before
+ * and recreate them. This hook allows you to execute code after the deletion
+ * of each instance.
+ *
+ * @param Drupal\recurring_events\Entity\EventSeries $event_series
+ *   The eventseries being altered.
+ * @param Drupal\recurring_events\Entity\EventInstance $event_instance
+ *   The event instance being deleted.
+ */
+function hook_recurring_events_save_post_instance_deletion(EventSeries $event_series, EventInstance $event_instance) {
+}
+
+/**
+ * Execute custom code before all instances are deleted.
+ *
+ * This hook differs to @see hook_recurring_events_save_pre_instances_deletion
+ * in that this hook fires before deleting instances by deleting the series
+ * rather than as a result of changing series date configuration.
+ */
+function hook_recurring_events_pre_delete_instances(EventSeries $event_series) {
+}
+
+/**
+ * Execute custom code after all instances are deleted.
+ *
+ * This hook differs to @see hook_recurring_events_save_post_instances_deletion
+ * in that this hook fires after deleting instances by deleting the series
+ * rather than as a result of changing series date configuration.
+ */
+function hook_recurring_events_post_delete_instance(EventSeries $event_series) {
+}
+
+/**
+ * Execute custom code before an instance is deleted.
+ *
+ * This hook differs to @see hook_recurring_events_save_pre_instance_deletion
+ * in that this hook fires before deleting an instance directly from the
+ * instance rather than as a result of changing series date configuration.
+ */
+function hook_recurring_events_pre_delete_instance(EventInstance $event_instance) {
+}
+
+/**
+ * Execute custom code after an instance is deleted.
+ *
+ * This hook differs to @see hook_recurring_events_save_post_instance_deletion
+ * in that this hook fires after deleting an instance directly from the
+ * instance rather than as a result of changing series date configuration.
+ */
+function hook_recurring_events_post_delete_instance(EventInstance $event_instance) {
+}
diff --git a/src/EventCreationService.php b/src/EventCreationService.php
index 6641d226a4b0b6f90dc15a56d79be472d4bf1c7f..534075cb8b36d4af951358e821984af10d8e4b27 100644
--- a/src/EventCreationService.php
+++ b/src/EventCreationService.php
@@ -76,7 +76,7 @@ class EventCreationService {
   }
 
   /**
-   * Check whether there have been recurring configuration changes.
+   * Check whether there have been form recurring configuration changes.
    *
    * @param Drupal\recurring_events\Entity\EventSeries $event
    *   The stored event series entity.
@@ -86,12 +86,29 @@ class EventCreationService {
    * @return bool
    *   TRUE if recurring config changes, FALSE otherwise.
    */
-  public function checkForRecurConfigChanges(EventSeries $event, FormStateInterface $form_state) {
+  public function checkForFormRecurConfigChanges(EventSeries $event, FormStateInterface $form_state) {
     $entity_config = $this->convertEntityConfigToArray($event);
     $form_config = $this->convertFormConfigToArray($form_state);
     return !(serialize($entity_config) === serialize($form_config));
   }
 
+  /**
+   * Check whether there have been original recurring configuration changes.
+   *
+   * @param Drupal\recurring_events\Entity\EventSeries $event
+   *   The stored event series entity.
+   * @param Drupal\recurring_events\Entity\EventSeries $original
+   *   The original stored event series entity.
+   *
+   * @return bool
+   *   TRUE if recurring config changes, FALSE otherwise.
+   */
+  public function checkForOriginalRecurConfigChanges(EventSeries $event, EventSeries $original) {
+    $entity_config = $this->convertEntityConfigToArray($event);
+    $original_config = $this->convertEntityConfigToArray($original);
+    return !(serialize($entity_config) === serialize($original_config));
+  }
+
   /**
    * Converts an EventSeries entity's recurring configuration to an array.
    *
@@ -137,6 +154,9 @@ class EventCreationService {
         $config['custom_dates'] = $event->getCustomDates();
         break;
     }
+
+    \Drupal::moduleHandler()->alter('recurring_events_entity_config_array', $config);
+
     return $config;
   }
 
@@ -245,6 +265,8 @@ class EventCreationService {
         break;
     }
 
+    \Drupal::moduleHandler()->alter('recurring_events_form_config_array', $config);
+
     return $config;
   }
 
@@ -378,6 +400,8 @@ class EventCreationService {
       }
     }
 
+    \Drupal::moduleHandler()->alter('recurring_events_diff_array', $diff);
+
     return $diff;
   }
 
@@ -401,18 +425,32 @@ class EventCreationService {
     }
     else {
       // If there are date differences, we need to clear out the instances.
-      $create_instances = $this->checkForRecurConfigChanges($original, $form_state);
+      $create_instances = $this->checkForFormRecurConfigChanges($original, $form_state);
       if ($create_instances) {
+        // Allow other modules to react prior to the deletion of all instances.
+        \Drupal::moduleHandler()->invokeAll('recurring_events_save_pre_instances_deletion', [$event, $form_state]);
+
         // Find all the instances and delete them.
         $instances = $event->event_instances->referencedEntities();
         if (!empty($instances)) {
           foreach ($instances as $index => $instance) {
+            // Allow other modules to react prior to deleting a specific
+            // instance after a date configuration change.
+            \Drupal::moduleHandler()->invokeAll('recurring_events_save_pre_instance_deletion', [$event, $instance]);
+
             $instance->delete();
+
+            // Allow other modules to react after deleting a specific instance
+            // after a date configuration change.
+            \Drupal::moduleHandler()->invokeAll('recurring_events_save_post_instance_deletion', [$event, $instance]);
           }
           $this->messenger->addStatus($this->translation->translate('A total of %count existing event instances were removed', [
             '%count' => count($instances),
           ]));
         }
+
+        // Allow other modules to react after the deletion of all instances.
+        \Drupal::moduleHandler()->invokeAll('recurring_events_save_post_instances_deletion', [$event, $form_state]);
       }
     }
 
diff --git a/src/Form/EventInstanceDeleteForm.php b/src/Form/EventInstanceDeleteForm.php
index e3ee660eac346742ba83cabce27d4c63862d9916..d6fdadea39e8cb86307996680449a4df6e32c2c6 100644
--- a/src/Form/EventInstanceDeleteForm.php
+++ b/src/Form/EventInstanceDeleteForm.php
@@ -141,8 +141,16 @@ class EventInstanceDeleteForm extends ContentEntityDeleteForm {
         $entity->getEventSeries()->save();
       }
 
+      // Allow other modules to react prior to deleting a specific instance
+      // after a date configuration change.
+      \Drupal::moduleHandler()->invokeAll('recurring_events_pre_delete_instance', [$entity]);
+
       $entity->delete();
 
+      // Allow other modules to react after deleting a specific instance after a
+      // date configuration change.
+      \Drupal::moduleHandler()->invokeAll('recurring_events_post_delete_instance', [$entity]);
+
       $start_date = $entity->date->start_date;
       \Drupal::logger('recurring_events')->notice('@type: deleted event instance of %title scheduled to begin on %date.',
         [
diff --git a/src/Form/EventSeriesDeleteForm.php b/src/Form/EventSeriesDeleteForm.php
index 1782775734d2d0409d3fd3bc5dfcdb7859fe3c74..b4ee5425194b4fe25702e7491dd7ea37a75b2ed1 100644
--- a/src/Form/EventSeriesDeleteForm.php
+++ b/src/Form/EventSeriesDeleteForm.php
@@ -165,10 +165,19 @@ class EventSeriesDeleteForm extends ContentEntityDeleteForm {
     else {
       $instances = $entity->event_instances->referencedEntities();
 
+      // Allow other modules to react prior to deleting all instances after a
+      // date configuration change.
+      \Drupal::moduleHandler()->invokeAll('recurring_events_pre_delete_instances', [$entity]);
+
       // Loop through all instances and remove them.
       foreach ($instances as $instance) {
         $instance->delete();
       }
+
+      // Allow other modules to react after deleting all instances after a date
+      // configuration change.
+      \Drupal::moduleHandler()->invokeAll('recurring_events_post_delete_instances', [$entity]);
+
       $entity->delete();
 
       \Drupal::logger('recurring_events')->notice('@type: deleted %title.',
diff --git a/src/Form/EventSeriesForm.php b/src/Form/EventSeriesForm.php
index baf3fcfe2e69ee70762306f8e31f19afc109b742..f7f3637a12802997475f1e07028de64d49d0c33b 100644
--- a/src/Form/EventSeriesForm.php
+++ b/src/Form/EventSeriesForm.php
@@ -200,7 +200,7 @@ class EventSeriesForm extends ContentEntityForm {
 
     if ($trigger['#id'] !== 'edit-confirm' && $editing) {
       $original = $this->storage->loadUnchanged($entity->id());
-      if ($this->creationService->checkForRecurConfigChanges($original, $form_state)) {
+      if ($this->creationService->checkForFormRecurConfigChanges($original, $form_state)) {
         $this->step = 1;
         $form_state->setRebuild(TRUE);
       }