diff --git a/composer.json b/composer.json
index 4d8036d..df516a7 100644
--- a/composer.json
+++ b/composer.json
@@ -2,6 +2,7 @@
   "name": "drupal/flysystem_s3",
   "require": {
     "league/flysystem": "^1.0.20",
-    "league/flysystem-aws-s3-v3": "^1.0, !=1.0.12, !=1.0.13"
+    "league/flysystem-aws-s3-v3": "^1.0, !=1.0.12, !=1.0.13",
+    "aws/aws-php-sns-message-validator": "^1.1"
   }
 }
diff --git a/flysystem_s3.routing.yml b/flysystem_s3.routing.yml
new file mode 100644
index 0000000..9339499
--- /dev/null
+++ b/flysystem_s3.routing.yml
@@ -0,0 +1,7 @@
+flysystem_s3.amazon_sns:
+  path: '/_flysystem-s3/sns'
+  defaults:
+    _title: 'Flysystem S3 SNS Notification'
+    _controller: 'Drupal\flysystem_s3\Controller\AmazonSnsController::receive'
+  requirements:
+    _access: 'TRUE'
diff --git a/src/Controller/AmazonSnsController.php b/src/Controller/AmazonSnsController.php
new file mode 100644
index 0000000..eee1b5e
--- /dev/null
+++ b/src/Controller/AmazonSnsController.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Drupal\flysystem_s3\Controller;
+
+use Aws\Sns\Exception\InvalidSnsMessageException;
+use Aws\Sns\Message;
+use Aws\Sns\MessageValidator;
+use Drupal\Component\Serialization\Json;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Respond to SNS notifications sent by Amazon.
+ */
+class AmazonSnsController {
+
+  /**
+   * Controller callback to process an SNS notification.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   *   The response. Typically SNS will resend if the response is not a 2XX.
+   *
+   * @throws \RuntimeException
+   *   Thrown when required headers are missing or the message does not
+   *   validate.
+   */
+  public function receive(Request $request) {
+    // Instantiate the Message and Validator
+    // TODO: Validate SNS topic
+    // Make sure the SNS-provided header exists.
+    if (!isset($request->headers) || !$request->headers->get('X_AMZ_SNS_MESSAGE_TYPE')) {
+      throw new \RuntimeException('SNS message type header not provided');
+    }
+
+    $data = Json::decode($request->getContent());
+    // TODO: Catch InvalidArgument.
+    $message = new Message($data);
+    $validator = new MessageValidator();
+
+    // Validate the message and log errors if invalid.
+    try {
+      $validator->validate($message);
+    }
+    catch (InvalidSnsMessageException $e) {
+      throw new \RuntimeException('Message not accepted');
+    }
+
+    // Check the type of the message and handle the subscription.
+    if ($message['Type'] === 'SubscriptionConfirmation') {
+      // Confirm the subscription by sending a GET request to the SubscribeURL.
+      file_get_contents($message['SubscribeURL']);
+    }
+
+    if ($message['Type'] == 'Notification') {
+      $n = Json::decode($message['Message']);
+      foreach ($n['Records'] as $record) {
+        if ($record['eventSource'] == 'aws:s3' && $record['eventName'] == 'ObjectRemoved:Delete') {
+          $s3 = $record['s3'];
+          $bucket = $s3['bucket']['name'];
+          $object = $s3['object']['key'];
+          $schemes = \Drupal::service('flysystem_factory')->getSchemes();
+          foreach ($schemes as $scheme) {
+            $settings = \Drupal::service('flysystem_factory')->getSettings($scheme);
+            if ($settings['driver'] == 's3' && $settings['config']['bucket'] == $bucket) {
+              $uri = $scheme . '://' . $object;
+              /** @var \Drupal\Core\Entity\EntityStorageInterface $s */
+              $s = \Drupal::service('entity_type.manager')->getStorage('file');
+              $files = $s->loadByProperties(['uri' => $uri]);
+              if (!empty($files)) {
+                $file = reset($files);
+                $file->delete();
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // We've successfully processed the response, so return a 200 so SNS doesn't
+    // retry the notification.
+    return new Response();
+  }
+
+}
