diff --git a/flag.info.yml b/flag.info.yml
index 99e76a1..b1de0fd 100644
--- a/flag.info.yml
+++ b/flag.info.yml
@@ -3,4 +3,6 @@ description: Create customized flags that users can set on entities.
 core: 8.x
 type: module
 package: Flags
+test_dependencies:
+ - rules:rules
 configure: entity.flag.collection
diff --git a/flag.rules.events.yml b/flag.rules.events.yml
new file mode 100644
index 0000000..03e824a
--- /dev/null
+++ b/flag.rules.events.yml
@@ -0,0 +1,6 @@
+flag.entity_flagged:
+  label: 'After flagging an entity'
+  deriver: '\Drupal\flag\Plugin\RulesEvent\EntityFlaggedDeriver'
+flag.entity_unflagged:
+  label: 'After unflagging an entity'
+  deriver: '\Drupal\flag\Plugin\RulesEvent\EntityUnflaggedDeriver'
diff --git a/flag.services.yml b/flag.services.yml
index 273fb9a..18bf0ea 100644
--- a/flag.services.yml
+++ b/flag.services.yml
@@ -26,3 +26,8 @@ services:
   flag.link_builder:
     class: Drupal\flag\FlagLinkBuilder
     arguments: ['@entity.manager', '@flag']
+  flag.rules_event_subscriber:
+      class: Drupal\flag\EventSubscriber\RulesEventSubscriber
+      arguments: ['@event_dispatcher']
+      tags:
+        - { name: 'event_subscriber' }
diff --git a/src/Event/FlagEventBase.php b/src/Event/FlagEventBase.php
index 1d88536..c920cbc 100644
--- a/src/Event/FlagEventBase.php
+++ b/src/Event/FlagEventBase.php
@@ -8,7 +8,6 @@ use Symfony\Component\EventDispatcher\Event;
 /**
  * Base Event from which other flag event are defined.
  */
-
 abstract class FlagEventBase extends Event {
 
   /**
@@ -32,7 +31,7 @@ abstract class FlagEventBase extends Event {
    * Get the flag entity related to the event.
    *
    * @return \Drupal\flag\FlagInterface
-   *  The flag related to the event.
+   *   The flag related to the event.
    */
   public function getFlag() {
     return $this->flag;
diff --git a/src/Event/FlaggingEvent.php b/src/Event/FlaggingEvent.php
index 5e437a3..fd7f7cc 100644
--- a/src/Event/FlaggingEvent.php
+++ b/src/Event/FlaggingEvent.php
@@ -21,7 +21,7 @@ class FlaggingEvent extends Event {
    * Builds a new FlaggingEvent.
    *
    * @param \Drupal\flag\FlaggingInterface $flagging
-   *   The flaging.
+   *   The flagging.
    */
   public function __construct(FlaggingInterface $flagging) {
     $this->flagging = $flagging;
diff --git a/src/Event/UnflaggingEvent.php b/src/Event/UnflaggingEvent.php
index acb2da0..b2925d0 100644
--- a/src/Event/UnflaggingEvent.php
+++ b/src/Event/UnflaggingEvent.php
@@ -10,24 +10,24 @@ use Symfony\Component\EventDispatcher\Event;
 class UnflaggingEvent extends Event {
 
   /**
-   * An array of flaggings.
+   * The flaggings in question.
    *
    * @var \Drupal\flag\FlaggingInterface[]
    */
-  protected $flaggings = [];
+  protected $flaggings;
 
   /**
    * Builds a new UnflaggingEvent.
    *
    * @param \Drupal\flag\FlaggingInterface[] $flaggings
-   *   The flaggings
+   *   The flaggings.
    */
   public function __construct(array $flaggings) {
     $this->flaggings = $flaggings;
   }
 
   /**
-   * Returns the flagging associated with the Event.
+   * Returns the flaggings associated with the Event.
    *
    * @return \Drupal\flag\FlaggingInterface[]
    *   The flaggings.
diff --git a/src/EventSubscriber/RulesEventSubscriber.php b/src/EventSubscriber/RulesEventSubscriber.php
new file mode 100644
index 0000000..08cf0fc
--- /dev/null
+++ b/src/EventSubscriber/RulesEventSubscriber.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Drupal\flag\EventSubscriber;
+
+use Drupal\flag\Event\FlagEvents;
+use Drupal\flag\Event\FlaggingEvent;
+use Drupal\flag\Event\UnflaggingEvent;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\EventDispatcher\GenericEvent;
+
+/**
+ * Subscribes to Flag events and dispatch Rules events.
+ */
+class RulesEventSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
+   * Constructor.
+   *
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
+   *   The event dispatcher.
+   */
+  public function __construct(EventDispatcherInterface $eventDispatcher) {
+    $this->eventDispatcher = $eventDispatcher;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events = [];
+    $events[FlagEvents::ENTITY_FLAGGED][] = 'onFlagged';
+    $events[FlagEvents::ENTITY_UNFLAGGED][] = 'onUnflagged';
+    return $events;
+  }
+
+  /**
+   * React on entity being flagged and dispatch rules-compatible events.
+   *
+   * @param \Drupal\flag\Event\FlaggingEvent $event
+   *   The flagging event.
+   * @param string $event_name
+   *   The event name.
+   */
+  public function onFlagged(FlaggingEvent $event, $event_name) {
+    $flagging = $event->getFlagging();
+    $rules_event = new GenericEvent($event->getFlagging(), [
+      'entity' => $flagging->getFlaggable(),
+      'flag' => $flagging->getFlag(),
+      'user' => $flagging->getOwner(),
+      'flagging' => $flagging,
+    ]);
+    $this->eventDispatcher->dispatch($event_name . ':' . $event->getFlagging()->getFlag()->id(), $rules_event);
+  }
+
+  /**
+   * React on entity being unflagged and dispatch rules-compatible events.
+   *
+   * @param \Drupal\flag\Event\UnflaggingEvent $event
+   *   The flagging event.
+   * @param string $event_name
+   *   The event name.
+   */
+  public function onUnflagged(UnflaggingEvent $event, $event_name) {
+    $flaggings = $event->getFlaggings();
+    foreach ($flaggings as $flagging) {
+      $rules_event = new GenericEvent($flagging, [
+        'entity' => $flagging->getFlaggable(),
+        'flag' => $flagging->getFlag(),
+        'user' => $flagging->getOwner(),
+        'flagging' => $flagging,
+      ]);
+      $this->eventDispatcher->dispatch($event_name . ':' . $flagging->getFlag()->id(), $rules_event);
+    }
+  }
+
+}
diff --git a/src/Plugin/RulesEvent/EntityFlagEventDeriverBase.php b/src/Plugin/RulesEvent/EntityFlagEventDeriverBase.php
new file mode 100644
index 0000000..9381401
--- /dev/null
+++ b/src/Plugin/RulesEvent/EntityFlagEventDeriverBase.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\flag\Plugin\RulesEvent;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\flag\FlagServiceInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Derives RulesEvent plugin definitions based on flag types.
+ */
+abstract class EntityFlagEventDeriverBase extends DeriverBase implements ContainerDeriverInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The flag service.
+   *
+   * @var \Drupal\flag\FlagServiceInterface
+   */
+  protected $flagService;
+
+  /**
+   * EntityFlaggedDeriver constructor.
+   *
+   * @param \Drupal\flag\FlagServiceInterface $flag_service
+   *   The flag service.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   */
+  public function __construct(FlagServiceInterface $flag_service, TranslationInterface $string_translation) {
+    $this->flagService = $flag_service;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('flag'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * Returns the action.
+   *
+   * @return string
+   *   The action.
+   */
+  abstract public function getAction();
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+
+    foreach ($this->flagService->getAllFlags() as $flag) {
+      $entity_type_id = $flag->getFlaggableEntityTypeId();
+      $event_definition = [
+        'category' => $this->t('Flag'),
+        'context' => [
+          'entity' => [
+            'type' => 'entity:' . $entity_type_id,
+            'label' => 'Flagged Entity',
+            'description' => 'The entity that was flagged.',
+          ],
+          'flag' => [
+            'type' => 'entity:flag',
+            'label' => 'Flag',
+            'description' => 'The flag used.',
+          ],
+          'user' => [
+            'type' => 'entity:user',
+            'label' => 'Flagging User',
+            'description' => 'The user who flagged the entity.',
+          ],
+          'flagging' => [
+            'type' => 'entity:flagging',
+            'label' => 'Flagging',
+            'description' => 'The action of flagging.',
+          ],
+        ],
+      ];
+      $this->derivatives[$flag->id()] = [
+        'label' => $this->t('A @entity_type has been @action, under "@flag_title"', [
+          '@entity_type' => $entity_type_id,
+          '@action' => $this->getAction(),
+          '@flag_title' => $flag->label(),
+        ]),
+      ] + $event_definition + $base_plugin_definition;
+    }
+    return $this->derivatives;
+  }
+
+}
diff --git a/src/Plugin/RulesEvent/EntityFlaggedDeriver.php b/src/Plugin/RulesEvent/EntityFlaggedDeriver.php
new file mode 100644
index 0000000..c21b538
--- /dev/null
+++ b/src/Plugin/RulesEvent/EntityFlaggedDeriver.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\flag\Plugin\RulesEvent;
+
+/**
+ * Extends EntityFlagEventDeriverBase plugin to provide flag events for Rules.
+ */
+class EntityFlaggedDeriver extends EntityFlagEventDeriverBase {
+
+  /**
+   * Returns the action.
+   *
+   * @return string
+   *   The action.
+   */
+  public function getAction() {
+    return $this->t('flagged');
+  }
+
+}
diff --git a/src/Plugin/RulesEvent/EntityUnflaggedDeriver.php b/src/Plugin/RulesEvent/EntityUnflaggedDeriver.php
new file mode 100644
index 0000000..70cb476
--- /dev/null
+++ b/src/Plugin/RulesEvent/EntityUnflaggedDeriver.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\flag\Plugin\RulesEvent;
+
+/**
+ * Extends EntityFlagEventDeriverBase plugin to provide unflag events for Rules.
+ */
+class EntityUnflaggedDeriver extends EntityFlagEventDeriverBase {
+
+  /**
+   * Returns the action.
+   *
+   * @return string
+   *   The action.
+   */
+  public function getAction() {
+    return $this->t('unflagged');
+  }
+
+}
diff --git a/tests/src/Kernel/FlagCountsTest.php b/tests/src/Kernel/FlagCountsTest.php
index 75f8256..fa1c597 100644
--- a/tests/src/Kernel/FlagCountsTest.php
+++ b/tests/src/Kernel/FlagCountsTest.php
@@ -1,8 +1,8 @@
 <?php
+
 namespace Drupal\Tests\flag\Kernel;
 
 use Drupal\flag\Entity\Flag;
-use Drupal\Tests\flag\Kernel\FlagKernelTestBase;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
 use Drupal\user\Entity\Role;
@@ -200,7 +200,8 @@ class FlagCountsTest extends FlagKernelTestBase {
     $this->flagService->flag($this->flag, $this->node, $this->anonymousUser, $anon1_session_id);
     $this->flagService->flag($this->flag, $this->node, $this->anonymousUser, $anon2_session_id);
 
-    // For non-global flags anonymous users can uniquely identifed by session_id.
+    // For non-global flags anonymous users can be uniquely
+    // identifed by session_id.
     $anon1_count = $this->flagCountService->getUserFlagFlaggingCount($this->flag, $this->anonymousUser, $anon1_session_id);
     $this->assertEqual($anon1_count, 1, "getUserFlagFlaggingCount() counts only the first user.");
     $anon2_count = $this->flagCountService->getUserFlagFlaggingCount($this->flag, $this->anonymousUser, $anon2_session_id);
@@ -210,7 +211,8 @@ class FlagCountsTest extends FlagKernelTestBase {
     $this->flag->setGlobal(TRUE);
     $this->flag->save();
 
-    // Despite being a global flag, queries about specific anonymous users can still be made.
+    // Despite being a global flag, queries about specific anonymous
+    // users can still be made.
     $rejected_count = $this->flagCountService->getUserFlagFlaggingCount($this->flag, $this->anonymousUser, $anon1_session_id);
     $this->assertEqual($rejected_count, 1, "getUserFlagFlaggingCount() ignores the session id.");
   }
diff --git a/tests/src/Kernel/FlagKernelTestBase.php b/tests/src/Kernel/FlagKernelTestBase.php
index c32414d..281ff11 100644
--- a/tests/src/Kernel/FlagKernelTestBase.php
+++ b/tests/src/Kernel/FlagKernelTestBase.php
@@ -1,7 +1,7 @@
 <?php
+
 namespace Drupal\Tests\flag\Kernel;
 
-use Drupal\Core\Session\AccountInterface;
 use Drupal\flag\FlagInterface;
 use Drupal\flag\Tests\FlagCreateTrait;
 use Drupal\KernelTests\KernelTestBase;
@@ -70,4 +70,5 @@ abstract class FlagKernelTestBase extends KernelTestBase {
 
     return \Drupal::entityTypeManager()->getStorage('flagging')->loadMultiple($ids);
   }
+
 }
diff --git a/tests/src/Kernel/FlagRulesEventIntegrationTest.php b/tests/src/Kernel/FlagRulesEventIntegrationTest.php
new file mode 100644
index 0000000..d7379f6
--- /dev/null
+++ b/tests/src/Kernel/FlagRulesEventIntegrationTest.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace Drupal\Tests\flag\Kernel;
+
+use Drupal\flag\Entity\Flag;
+use Drupal\rules\Context\ContextConfig;
+
+/**
+ * Test for the flag Symfony events mapping to Rules events.
+ *
+ * @group flag
+ * @group legacy
+ * @todo Remove the 'legacy' tag when Rules no longer uses deprecated code.
+ * @see https://www.drupal.org/project/scheduler/issues/2924353
+ *
+ * @requires module rules
+ */
+class FlagRulesEventIntegrationTest extends FlagKernelTestBase {
+
+  /**
+   * The entity storage for Rules config entities.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $storage;
+
+  /**
+   * The expression plugin manager.
+   *
+   * @var \Drupal\rules\Engine\ExpressionManager
+   */
+  protected $expressionManager;
+
+  /**
+   * Rules logger.
+   *
+   * @var \Drupal\rules\Logger\RulesLoggerChannel
+   */
+  protected $logger;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['rules', 'rules_test', 'typed_data'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->storage = $this->container->get('entity_type.manager')
+      ->getStorage('rules_reaction_rule');
+
+    $this->logger = $this->container->get('logger.channel.rules');
+
+    // Clear the log from any stale entries that are bleeding over from previous
+    // tests.
+    $this->logger->clearLogs();
+
+    $this->expressionManager = $this->container
+      ->get('plugin.manager.rules_expression');
+  }
+
+  /**
+   * Test that flagging and unflagging an entity triggers Rules event.
+   */
+  public function testEntityFlaggingEvent() {
+    // Create a node to be flagged.
+    $entity_type_manager = $this->container->get('entity_type.manager');
+    $entity_type_manager->getStorage('node_type')
+      ->create(['type' => 'page'])
+      ->save();
+
+    $node = $entity_type_manager->getStorage('node')
+      ->create([
+        'title' => 'test_node',
+        'type' => 'page',
+      ]);
+    $node->save();
+
+    // Create a flag.
+    $flag = Flag::create([
+      'id' => strtolower($this->randomMachineName()),
+      'label' => $this->randomString(),
+      'entity_type' => 'node',
+      'bundles' => ['page'],
+      'flag_type' => 'entity:node',
+      'link_type' => 'reload',
+      'flagTypeConfig' => [],
+      'linkTypeConfig' => [],
+    ]);
+    $flag->save();
+
+    // Rule for entity_flagged event.
+    $rule0 = $this->expressionManager->createRule();
+    $rule0->addCondition('rules_test_true');
+    $rule0->addAction('rules_test_log',
+      ContextConfig::create()
+        ->map('message', 'entity.title.0.value'));
+
+    // Rule for entity_unflagged event.
+    $rule1 = $this->expressionManager->createRule();
+    $rule1->addCondition('rules_test_true');
+    $rule1->addAction('rules_test_log',
+      ContextConfig::create()
+        ->map('message', 'user.name.0.value'));
+
+    $events = [
+      'flag.entity_flagged:' . $flag->id(),
+      'flag.entity_unflagged:' . $flag->id(),
+    ];
+    foreach ($events as $index => $event_name) {
+      $config_entity = $this->storage->create([
+        'id' => 'test_rule' . $index,
+        'events' => [['event_name' => $event_name]],
+        'expression' => ${'rule' . $index}->getConfiguration(),
+      ]);
+      $config_entity->save();
+    }
+
+    // Create a user for flagging.
+    $account = $this->createUser([], 'test_user');
+
+    // The logger instance has changed, refresh it.
+    $this->logger = $this->container->get('logger.channel.rules');
+
+    // Flag the entity to trigger flagged events.
+    $this->flagService->flag($flag, $node, $account);
+
+    // Test that the action in the rule logged flaggable node title.
+    $this->assertRulesLogEntryExists('test_node');
+
+    // Flag the entity to trigger unflagged events.
+    $this->flagService->unflag($flag, $node, $account);
+
+    // Test that the action in the rule logged flaggable owner username.
+    $this->assertRulesLogEntryExists('test_user', 1);
+  }
+
+  /**
+   * Checks if particular message is in the log with given delta.
+   *
+   * @param string $message
+   *   Log message.
+   * @param int $log_item_index
+   *   Log item's index in log entries stack.
+   */
+  protected function assertRulesLogEntryExists($message, $log_item_index = 0) {
+    // Test that the action has logged something.
+    $logs = $this->logger->getLogs();
+    $this->assertEqual($logs[$log_item_index]['message'], $message);
+  }
+
+}
diff --git a/tests/src/Kernel/FlagServiceTest.php b/tests/src/Kernel/FlagServiceTest.php
index 49e0191..7ec23a4 100644
--- a/tests/src/Kernel/FlagServiceTest.php
+++ b/tests/src/Kernel/FlagServiceTest.php
@@ -1,4 +1,5 @@
 <?php
+
 namespace Drupal\Tests\flag\Kernel;
 
 use Drupal\flag\Entity\Flag;
@@ -167,6 +168,7 @@ class FlagServiceTest extends FlagKernelTestBase {
     catch (\LogicException $e){
       $this->fail('The unfag() method threw an exception where processing a valid unflag request.');
     }
+
   }
 
   /**
@@ -174,7 +176,7 @@ class FlagServiceTest extends FlagKernelTestBase {
    */
   public function testFlagServiceGetFlaggingUsers() {
     // The service methods don't check access, so our user can be anybody.
-    $accounts = array($this->createUser(), $this->createUser());
+    $accounts = [$this->createUser(), $this->createUser()];
 
     // Create a flag.
     $flag = Flag::create([
diff --git a/tests/src/Unit/Integration/Event/FlagEventsTest.php b/tests/src/Unit/Integration/Event/FlagEventsTest.php
new file mode 100644
index 0000000..b5626d3
--- /dev/null
+++ b/tests/src/Unit/Integration/Event/FlagEventsTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\Tests\flag\Unit\Integration\Event;
+
+use Drupal\flag\FlagInterface;
+use Drupal\flag\FlagServiceInterface;
+use Drupal\rules\Core\RulesEventManager;
+use Drupal\Tests\rules\Unit\Integration\Event\EventTestBase;
+
+/**
+ * Checks that the Rules events are defined.
+ *
+ * @coversDefaultClass Drupal\flag\Plugin\RulesEvent\EntityFlaggedDeriver
+ *
+ * @group flag
+ * @group legacy
+ * @todo Remove the 'legacy' tag when Rules no longer uses deprecated code.
+ * @see https://www.drupal.org/project/scheduler/issues/2924353
+ *
+ * @requires module rules
+ */
+class FlagEventsTest extends EventTestBase {
+
+  /**
+   * The flag service mock.
+   *
+   * @var \Drupal\flag\FlagServiceInterface
+   */
+  protected $flagService;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Mock a FlagInterface object with 'test_flag' id.
+    $flag = $this->prophesize(FlagInterface::class);
+    $flag->id()->willReturn('test_flag');
+    $flag->getFlaggableEntityTypeId()->willReturn('node');
+    $flag->label()->willReturn('Cheesy');
+
+    // Mock a FlagServiceInterface and inject it into the container.
+    $this->flagService = $this->prophesize(FlagServiceInterface::class);
+    $this->flagService->getAllFlags()->willReturn([$flag]);
+    $this->container->set('flag', $this->flagService->reveal());
+
+    $this->enabledModules['flag'] = TRUE;
+    $this->moduleHandler->getModuleDirectories()
+      ->willReturn(['flag' => __DIR__ . '/../../../../../']);
+    $this->eventManager = new RulesEventManager($this->moduleHandler->reveal());
+  }
+
+  /**
+   * Test the event metadata is generated.
+   */
+  public function testEventMetadata() {
+    $plugin_definition = $this->eventManager->getDefinition('flag.entity_flagged:test_flag');
+    $this->assertSame('A node has been flagged, under "Cheesy"', (string) $plugin_definition['label']);
+    $context_definition = $plugin_definition['context']['entity'];
+    $this->assertSame('entity:node', $context_definition->getDataType());
+    $this->assertSame('Flagged Entity', $context_definition->getLabel());
+  }
+
+}
