diff --git a/classes/event/attempt_submitted.php b/classes/event/attempt_submitted.php
new file mode 100644
index 0000000000000000000000000000000000000000..8392b2469d4f9550457ccae5ec81d468c29e0689
--- /dev/null
+++ b/classes/event/attempt_submitted.php
@@ -0,0 +1,46 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Attempt submitted event
+ *
+ * @package    mod_hvp
+ * @copyright  2020 Joubel AS <contact@joubel.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace mod_hvp\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The mod_hvp instance list viewed event class.
+ *
+ * @package    mod_hvp
+ * @copyright  @copyright  2016 Joubel AS <contact@joubel.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class attempt_submitted extends \core\event\base
+{
+
+    /**
+     * @inheritDoc
+     */
+    protected function init() {
+        $this->data['crud'] = 'u';
+        $this->data['edulevel'] = self::LEVEL_PARTICIPATING;
+    }
+}
diff --git a/classes/user_grades.php b/classes/user_grades.php
index 103ea6db39a3b06e343a83f5010c18344620aa81..eca660a53fffd6286bc1b2068ccc31713fee2701 100644
--- a/classes/user_grades.php
+++ b/classes/user_grades.php
@@ -96,6 +96,12 @@ class user_grades {
                 $content->name, $content->major_version . '.' . $content->minor_version
         );
 
+        // Trigger Moodle event for async notification messages
+        $event = \mod_hvp\event\attempt_submitted::create([
+            'context' => $context,
+        ]);
+        $event->trigger();
+
         \H5PCore::ajaxSuccess();
     }
 
diff --git a/db/access.php b/db/access.php
index 84757113d3aed7fa107476ec2d8a331b0d4b34e4..5658c6e3f18a34858c7439111a9a92fcabe6d0fb 100644
--- a/db/access.php
+++ b/db/access.php
@@ -173,4 +173,18 @@ $capabilities = array(
         )
     ),
 
+    // Receive a confirmation message of own h5p submission.
+    'mod/hvp:emailconfirmsubmission' => array(
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array()
+    ),
+
+    // Receive a notification message of other peoples' h5p submissions.
+    'mod/hvp:emailnotifysubmission' => array(
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array()
+    ),
+
 );
diff --git a/db/events.php b/db/events.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6f7983bf08934a3d025dc600068932fd531ce3f
--- /dev/null
+++ b/db/events.php
@@ -0,0 +1,38 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Add event handlers for H5P
+ *
+ * @package    mod_hvp
+ * @category   event
+ * @copyright  2020 Joubel AS <contact@joubel.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+$observers = array(
+
+    // Handle attempt submitted event, as a way to send confirmation messages asynchronously.
+    array(
+        'eventname'   => '\mod_hvp\event\attempt_submitted',
+        'includefile' => '/mod/hvp/locallib.php',
+        'callback'    => 'hvp_attempt_submitted_handler',
+        'internal'    => false
+    ),
+);
diff --git a/db/messages.php b/db/messages.php
new file mode 100644
index 0000000000000000000000000000000000000000..a08d8f0d56f009f42ece8d954c76393312cd1ed4
--- /dev/null
+++ b/db/messages.php
@@ -0,0 +1,40 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Defines message providers (types of message sent) for the hvp module.
+ *
+ * @package   mod_hvp
+ * @copyright 2020 Joubel AS <contact@joubel.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$messageproviders = array(
+    // Notify teacher that a student has submitted an attempt.
+    'submission' => array(
+        'capability' => 'mod/hvp:emailnotifysubmission'
+    ),
+
+    // Confirm a student's quiz attempt.
+    'confirmation' => array(
+        'capability' => 'mod/hvp:emailconfirmsubmission',
+        'defaults' => array(
+            'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF,
+        ),
+    ),
+);
diff --git a/lang/en/hvp.php b/lang/en/hvp.php
index 7eaa5755b0d4c0916f4a56556157f2524bfe70d3..ce26356cae3313d45e9937fedbba0b7159358a4f 100644
--- a/lang/en/hvp.php
+++ b/lang/en/hvp.php
@@ -225,6 +225,8 @@ $string['hvp:userestrictedlibraries'] = 'Use restricted H5P content types';
 $string['hvp:updatelibraries'] = 'Install new H5P content types or update existing ones';
 $string['hvp:getcachedassets'] = 'Required for viewing H5P activities';
 $string['hvp:installrecommendedh5plibraries'] = 'Install new safe H5P content types recommended by H5P.org';
+$string['hvp:emailconfirmsubmission'] = 'Get a confirmation message when submitting';
+$string['hvp:emailnotifysubmission'] = 'Get a notification message when an attempt is submitted';
 
 // Capabilities error messages.
 $string['nopermissiontogettranslations'] = 'You do not have permissions to retrieve translations';
@@ -448,4 +450,24 @@ $string['unpackedfilesexceedsmaxsize'] = 'The total size of the unpacked files e
 $string['couldnotreadfilefromzip'] = 'Unable to read file from the package: %fileName';
 $string['couldnotparsejsonfromzip'] = 'Unable to parse JSON from the package: %fileName';
 $string['couldnotparsepostdata'] = 'Could not parse post data.';
-$string['nombstringexteension'] = 'The mbstring PHP extension is not loaded. H5P needs this to function properly';
\ No newline at end of file
+$string['nombstringexteension'] = 'The mbstring PHP extension is not loaded. H5P needs this to function properly';
+
+// Messaging api
+$string['messageprovider:confirmation'] = 'Confirmation of your own H5P submissions';
+$string['messageprovider:submission'] = 'Notification of H5P submissions';
+$string['emailnotifysubject'] = '{$a->studentname} has completed {$a->hvpname}';
+$string['emailnotifybody'] = 'Hi {$a->username},
+
+{$a->studentname} has completed \'{$a->hvpname}\' ({$a->hvpurl}) in course \'{$a->coursename}\'.
+
+You can review this attempt at {$a->hvpreporturl}.';
+$string['emailnotifysmall'] = '{$a->studentname} has completed {$a->hvpname}. See {$a->hvpreporturl}';
+$string['emailconfirmbody'] = 'Hi {$a->username},
+
+Thank you for submitting your answers to \'{$a->hvpname}\' in course \'{$a->coursename}\'.
+
+This message confirms that your answers have been saved.
+
+You can access this H5P at {$a->hvpurl}.';
+$string['emailconfirmsmall'] = 'Thank you for submitting your answers to \'{$a->hvpname}\'';
+$string['emailconfirmsubject'] = 'Submission confirmation: {$a->hvpname}';
diff --git a/locallib.php b/locallib.php
index 72f7eee24911a3c90859d9282dbcb0744bc7643b..f75e4d344ad687d1e46070e57bbc9fe495980413 100644
--- a/locallib.php
+++ b/locallib.php
@@ -23,6 +23,9 @@
  * @copyright  2016 Joubel AS <contact@joubel.com>
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
+
+use core\message\message;
+
 defined('MOODLE_INTERNAL') || die();
 
 require_once('autoloader.php');
@@ -459,3 +462,192 @@ function hvp_require_view_results_permission($userid, $context, $redirectcontent
         }
     }
 }
+
+/**
+ * Sends notification messages to the interested parties that assign the role capability
+ *
+ * @param object $recipient user object of the intended recipient
+ * @param $submitter
+ * @param object $a associative array of replaceable fields for the templates
+ *
+ * @return int|false as for {@link message_send()}.
+ * @throws coding_exception
+ */
+function hvp_send_notification($recipient, $submitter, $a) {
+    // Recipient info for template.
+    $a->useridnumber = $recipient->id;
+    $a->username     = fullname($recipient);
+    $a->userusername = $recipient->username;
+
+    // Prepare the message.
+    $message                    = new message();
+    $message->component         = 'mod_hvp';
+    $message->name              = 'submission';
+    $message->userfrom          = $submitter;
+    $message->userto            = $recipient;
+    $message->subject           = get_string('emailnotifysubject', 'hvp', $a);
+    $message->fullmessage       = get_string('emailnotifybody', 'hvp', $a);
+    $message->fullmessageformat = FORMAT_PLAIN;
+    $message->fullmessagehtml   = '';
+    $message->smallmessage      = get_string('emailnotifysmall', 'hvp', $a);
+    $message->courseid          = $a->courseid;
+
+    $message->contexturl     = $a->hvpreporturl;
+    $message->contexturlname = $a->hvpname;
+
+    return message_send($message);
+}
+
+/**
+ * Sends a confirmation message to the student confirming that the attempt was processed.
+ *
+ * @param object $a useful information that can be used in the message
+ *      subject and body.
+ *
+ * @return int|false as for {@link message_send()}.
+ * @throws coding_exception
+ */
+function hvp_send_confirmation($recipient, $a) {
+    // Add information about the recipient to $a.
+    $a->username     = fullname($recipient);
+    $a->userusername = $recipient->username;
+
+    // Prepare the message.
+    $eventdata               = new \core\message\message();
+    $eventdata->courseid     = $a->courseid;
+    $eventdata->component    = 'mod_hvp';
+    $eventdata->name         = 'confirmation';
+    $eventdata->notification = 1;
+
+    $eventdata->userfrom          = core_user::get_noreply_user();
+    $eventdata->userto            = $recipient;
+    $eventdata->subject           = get_string('emailconfirmsubject', 'hvp', $a);
+    $eventdata->fullmessage       = get_string('emailconfirmbody', 'hvp', $a);
+    $eventdata->fullmessageformat = FORMAT_PLAIN;
+    $eventdata->fullmessagehtml   = '';
+
+    $eventdata->smallmessage   = get_string('emailconfirmsmall', 'hvp', $a);
+    $eventdata->contexturl     = $a->hvpurl;
+    $eventdata->contexturlname = $a->hvpname;
+
+    return message_send($eventdata);
+}
+
+/**
+ * Send all the required messages when a h5p attempt is submitted.
+ *
+ * @param object $course the course
+ * @param object $hvp the h5p
+ * @param object $attempt this attempt just finished
+ * @param context $context the h5p context
+ * @param object $cm the coursemodule for this h5p
+ *
+ * @return bool true if all necessary messages were sent successfully, else false.
+ * @throws coding_exception
+ * @throws dml_exception
+ */
+function hvp_send_notification_messages($course, $hvp, $attempt, $context, $cm) {
+    global $CFG, $DB;
+
+    // Do nothing if required objects not present.
+    if (empty($course) or empty($hvp) or empty($attempt) or empty($context)) {
+        throw new coding_exception('$course, $hvp, $attempt, $context and $cm must all be set.');
+    }
+
+    $submitter = $DB->get_record('user', array('id' => $attempt->userid), '*', MUST_EXIST);
+
+    // Check for confirmation required.
+    $sendconfirm        = false;
+    $notifyexcludeusers = '';
+    if (has_capability('mod/hvp:emailconfirmsubmission', $context, $submitter, false)) {
+        $notifyexcludeusers = $submitter->id;
+        $sendconfirm        = true;
+    }
+
+    // Check for notifications required.
+    $notifyfields = 'u.id, u.username, u.idnumber, u.email, u.emailstop, u.lang,
+            u.timezone, u.mailformat, u.maildisplay, u.auth, u.suspended, u.deleted, ';
+    $notifyfields .= get_all_user_name_fields(true, 'u');
+    $groups       = groups_get_all_groups($course->id, $submitter->id, $cm->groupingid);
+    if (is_array($groups) && count($groups) > 0) {
+        $groups = array_keys($groups);
+    } else if (groups_get_activity_groupmode($cm, $course) != NOGROUPS) {
+        // If the user is not in a group, and the hvp is set to group mode,
+        // then set $groups to a non-existant id so that only users with
+        // 'moodle/site:accessallgroups' get notified.
+        $groups = - 1;
+    } else {
+        $groups = '';
+    }
+    $userstonotify = get_users_by_capability($context, 'mod/hvp:emailnotifysubmission',
+        $notifyfields, '', '', '', $groups, $notifyexcludeusers, false, false, true);
+
+    if (empty($userstonotify) && !$sendconfirm) {
+        return true; // Nothing to do.
+    }
+
+    $a = new stdClass();
+    // Course info.
+    $a->courseid        = $course->id;
+    $a->coursename      = $course->fullname;
+    $a->courseshortname = $course->shortname;
+
+    // H5P info.
+    $a->hvpname       = $hvp->name;
+    $report           = "{$CFG->wwwroot}/mod/hvp/review.php?id={$hvp->id}&course={$course->id}&user={$submitter->id}";
+    $a->hvpreporturl  = $report;
+    $a->hvpreportlink = '<a href="' . $a->hvpreporturl . '">' .
+                        format_string($hvp->name, true, ['context' => $context]) . ' report</a>';
+    $a->hvpurl        = $CFG->wwwroot . '/mod/hvp/view.php?id=' . $cm->id;
+    $a->hvplink       = '<a href="' . $a->hvpurl . '">' .
+                        format_string($hvp->name, true, ['context' => $context]) . '</a>';
+    $a->hvpid         = $hvp->id;
+    $a->hvpcmid       = $cm->id;
+
+    // Student who sat the hvp info.
+    $a->studentidnumber = $submitter->id;
+    $a->studentname     = fullname($submitter);
+    $a->studentusername = $submitter->username;
+
+    $allok = true;
+
+    // Send notifications if required.
+    if (!empty($userstonotify)) {
+        foreach ($userstonotify as $recipient) {
+            $allok = $allok && hvp_send_notification($recipient, $submitter, $a);
+        }
+    }
+
+    // Send confirmation if required. We send the student confirmation last, so
+    // that if message sending is being intermittently buggy, which means we send
+    // some but not all messages, and then try again later, then teachers may get
+    // duplicate messages, but the student will always get exactly one.
+    if ($sendconfirm) {
+        $allok = $allok && hvp_send_confirmation($submitter, $a);
+    }
+
+    return $allok;
+}
+
+/**
+ * Callback for the attempt_submitted event.
+ * Sends out notification messages.
+ *
+ * @param $event
+ *
+ * @throws coding_exception
+ * @throws dml_exception
+ */
+function hvp_attempt_submitted_handler($event) {
+    global $DB, $PAGE;
+    $course  = $DB->get_record('course', array('id' => $event->courseid));
+    $cm      = get_coursemodule_from_id('hvp', $event->get_context()->instanceid, $event->courseid);
+    $hvp     = $DB->get_record('hvp', array('id' => $cm->instance));
+    $attempt = (object) [
+        'userid' => $event->userid
+    ];
+    $context = context_module::instance($cm->id);
+    $PAGE->set_context($context);
+
+    hvp_send_notification_messages($course, $hvp, $attempt, $context, $cm);
+}
\ No newline at end of file
diff --git a/version.php b/version.php
index b4b69751dcedee61431601b5523b1b9bbbdad215..90d9ba2d7804730d68a2a20daa66d50e137dc2ab 100644
--- a/version.php
+++ b/version.php
@@ -23,7 +23,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2019111300;
+$plugin->version   = 2020013103;
 $plugin->requires  = 2013051403;
 $plugin->cron      = 0;
 $plugin->component = 'mod_hvp';