diff --git a/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs b/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs
index 5dc996c2e..d4cb95ee6 100644
--- a/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs
+++ b/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs
@@ -1,8 +1,10 @@
 using System;
+using System.Collections.Generic;
 using FluentAssertions;
 using FluentValidation.Results;
 using NUnit.Framework;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.MediaFiles;
 using NzbDrone.Core.Notifications;
 using NzbDrone.Core.ThingiProvider;
 using NzbDrone.Core.Validation;
@@ -57,7 +59,7 @@ namespace NzbDrone.Core.Test.NotificationTests
                 TestLogger.Info("OnDownload was called");
             }
 
-            public override void OnRename(Author author)
+            public override void OnRename(Author author, List<RenamedBookFile> renamedFiles)
             {
                 TestLogger.Info("OnRename was called");
             }
diff --git a/src/NzbDrone.Core.Test/NotificationTests/SynologyIndexerFixture.cs b/src/NzbDrone.Core.Test/NotificationTests/SynologyIndexerFixture.cs
index ce5569a75..d0c4803c1 100644
--- a/src/NzbDrone.Core.Test/NotificationTests/SynologyIndexerFixture.cs
+++ b/src/NzbDrone.Core.Test/NotificationTests/SynologyIndexerFixture.cs
@@ -65,7 +65,7 @@ namespace NzbDrone.Core.Test.NotificationTests
         {
             (Subject.Definition.Settings as SynologyIndexerSettings).UpdateLibrary = false;
 
-            Subject.OnRename(_author);
+            Subject.OnRename(_author, new List<RenamedBookFile>());
 
             Mocker.GetMock<ISynologyIndexerProxy>()
                 .Verify(v => v.UpdateFolder(_author.Path), Times.Never());
@@ -95,7 +95,7 @@ namespace NzbDrone.Core.Test.NotificationTests
         [Test]
         public void should_update_entire_series_folder_on_rename()
         {
-            Subject.OnRename(_author);
+            Subject.OnRename(_author, new List<RenamedBookFile>());
 
             Mocker.GetMock<ISynologyIndexerProxy>()
                 .Verify(v => v.UpdateFolder(@"C:\Test\".AsOsAgnostic()), Times.Once());
diff --git a/src/NzbDrone.Core/MediaFiles/Events/AuthorRenamedEvent.cs b/src/NzbDrone.Core/MediaFiles/Events/AuthorRenamedEvent.cs
index 5d95e1982..3dae8fc97 100644
--- a/src/NzbDrone.Core/MediaFiles/Events/AuthorRenamedEvent.cs
+++ b/src/NzbDrone.Core/MediaFiles/Events/AuthorRenamedEvent.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
 using NzbDrone.Common.Messaging;
 using NzbDrone.Core.Books;
 
@@ -6,10 +7,12 @@ namespace NzbDrone.Core.MediaFiles.Events
     public class AuthorRenamedEvent : IEvent
     {
         public Author Author { get; private set; }
+        public List<RenamedBookFile> RenamedFiles { get; private set; }
 
-        public AuthorRenamedEvent(Author author)
+        public AuthorRenamedEvent(Author author, List<RenamedBookFile> renamedFiles)
         {
             Author = author;
+            RenamedFiles = renamedFiles;
         }
     }
 }
diff --git a/src/NzbDrone.Core/MediaFiles/RenameBookFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameBookFileService.cs
index 94df44e46..79c4dc0e7 100644
--- a/src/NzbDrone.Core/MediaFiles/RenameBookFileService.cs
+++ b/src/NzbDrone.Core/MediaFiles/RenameBookFileService.cs
@@ -115,12 +115,12 @@ namespace NzbDrone.Core.MediaFiles
         {
             var allFiles = _mediaFileService.GetFilesByAuthor(author.Id);
             var counts = allFiles.GroupBy(x => x.EditionId).ToDictionary(g => g.Key, g => g.Count());
-            var renamed = new List<BookFile>();
+            var renamed = new List<RenamedBookFile>();
 
             // Don't rename Calibre files
             foreach (var bookFile in bookFiles.Where(x => x.CalibreId == 0))
             {
-                var bookFilePath = bookFile.Path;
+                var previousPath = bookFile.Path;
                 bookFile.PartCount = counts[bookFile.EditionId];
 
                 try
@@ -129,11 +129,16 @@ namespace NzbDrone.Core.MediaFiles
                     _bookFileMover.MoveBookFile(bookFile, author);
 
                     _mediaFileService.Update(bookFile);
-                    renamed.Add(bookFile);
+
+                    renamed.Add(new RenamedBookFile
+                    {
+                        BookFile = bookFile,
+                        PreviousPath = previousPath
+                    });
 
                     _logger.Debug("Renamed book file: {0}", bookFile);
 
-                    _eventAggregator.PublishEvent(new BookFileRenamedEvent(author, bookFile, bookFilePath));
+                    _eventAggregator.PublishEvent(new BookFileRenamedEvent(author, bookFile, previousPath));
                 }
                 catch (FileAlreadyExistsException ex)
                 {
@@ -145,13 +150,13 @@ namespace NzbDrone.Core.MediaFiles
                 }
                 catch (Exception ex)
                 {
-                    _logger.Error(ex, "Failed to rename file {0}", bookFilePath);
+                    _logger.Error(ex, "Failed to rename file {0}", previousPath);
                 }
             }
 
             if (renamed.Any())
             {
-                _eventAggregator.PublishEvent(new AuthorRenamedEvent(author));
+                _eventAggregator.PublishEvent(new AuthorRenamedEvent(author, renamed));
 
                 _logger.Debug("Removing Empty Subfolders from: {0}", author.Path);
                 _diskProvider.RemoveEmptySubfolders(author.Path);
diff --git a/src/NzbDrone.Core/MediaFiles/RenamedBookFile.cs b/src/NzbDrone.Core/MediaFiles/RenamedBookFile.cs
new file mode 100644
index 000000000..1392bd749
--- /dev/null
+++ b/src/NzbDrone.Core/MediaFiles/RenamedBookFile.cs
@@ -0,0 +1,8 @@
+namespace NzbDrone.Core.MediaFiles
+{
+    public class RenamedBookFile
+    {
+        public BookFile BookFile { get; set; }
+        public string PreviousPath { get; set; }
+    }
+}
diff --git a/src/NzbDrone.Core/Notifications/BookDownloadMessage.cs b/src/NzbDrone.Core/Notifications/BookDownloadMessage.cs
index a3c036bf2..3f1d4403e 100644
--- a/src/NzbDrone.Core/Notifications/BookDownloadMessage.cs
+++ b/src/NzbDrone.Core/Notifications/BookDownloadMessage.cs
@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.Download;
 using NzbDrone.Core.MediaFiles;
 
 namespace NzbDrone.Core.Notifications
@@ -11,7 +12,7 @@ namespace NzbDrone.Core.Notifications
         public Book Book { get; set; }
         public List<BookFile> BookFiles { get; set; }
         public List<BookFile> OldFiles { get; set; }
-        public string DownloadClient { get; set; }
+        public DownloadClientItemClientInfo DownloadClientInfo { get; set; }
         public string DownloadId { get; set; }
 
         public override string ToString()
diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs
index 89b52a223..9126936db 100644
--- a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs
+++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs
@@ -10,6 +10,7 @@ using NzbDrone.Common.Processes;
 using NzbDrone.Common.Serializer;
 using NzbDrone.Core.Books;
 using NzbDrone.Core.HealthCheck;
+using NzbDrone.Core.MediaFiles;
 using NzbDrone.Core.ThingiProvider;
 using NzbDrone.Core.Validation;
 
@@ -37,7 +38,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
         public override void OnGrab(GrabMessage message)
         {
             var author = message.Author;
-            var remoteBook = message.Book;
+            var remoteBook = message.RemoteBook;
             var releaseGroup = remoteBook.ParsedBookInfo.ReleaseGroup;
             var environmentVariables = new StringDictionary();
 
@@ -56,7 +57,8 @@ namespace NzbDrone.Core.Notifications.CustomScript
             environmentVariables.Add("Readarr_Release_Quality", remoteBook.ParsedBookInfo.Quality.Quality.Name);
             environmentVariables.Add("Readarr_Release_QualityVersion", remoteBook.ParsedBookInfo.Quality.Revision.Version.ToString());
             environmentVariables.Add("Readarr_Release_ReleaseGroup", releaseGroup ?? string.Empty);
-            environmentVariables.Add("Readarr_Download_Client", message.DownloadClient ?? string.Empty);
+            environmentVariables.Add("Readarr_Download_Client", message.DownloadClientName ?? string.Empty);
+            environmentVariables.Add("Readarr_Download_Client_Type", message.DownloadClientType ?? string.Empty);
             environmentVariables.Add("Readarr_Download_Id", message.DownloadId ?? string.Empty);
 
             ExecuteScript(environmentVariables);
@@ -77,7 +79,8 @@ namespace NzbDrone.Core.Notifications.CustomScript
             environmentVariables.Add("Readarr_Book_Title", book.Title);
             environmentVariables.Add("Readarr_Book_GRId", book.Editions.Value.Single(e => e.Monitored).ForeignEditionId.ToString());
             environmentVariables.Add("Readarr_Book_ReleaseDate", book.ReleaseDate.ToString());
-            environmentVariables.Add("Readarr_Download_Client", message.DownloadClient ?? string.Empty);
+            environmentVariables.Add("Readarr_Download_Client", message.DownloadClientInfo?.Name ?? string.Empty);
+            environmentVariables.Add("Readarr_Download_Client_Type", message.DownloadClientInfo?.Type ?? string.Empty);
             environmentVariables.Add("Readarr_Download_Id", message.DownloadId ?? string.Empty);
 
             if (message.BookFiles.Any())
@@ -94,7 +97,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
             ExecuteScript(environmentVariables);
         }
 
-        public override void OnRename(Author author)
+        public override void OnRename(Author author, List<RenamedBookFile> renamedFiles)
         {
             var environmentVariables = new StringDictionary();
 
diff --git a/src/NzbDrone.Core/Notifications/Discord/Discord.cs b/src/NzbDrone.Core/Notifications/Discord/Discord.cs
index 31cbd69a6..55b29f97e 100644
--- a/src/NzbDrone.Core/Notifications/Discord/Discord.cs
+++ b/src/NzbDrone.Core/Notifications/Discord/Discord.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using FluentValidation.Results;
 using NzbDrone.Common.Extensions;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.MediaFiles;
 using NzbDrone.Core.Notifications.Discord.Payloads;
 using NzbDrone.Core.Validation;
 
@@ -54,7 +55,7 @@ namespace NzbDrone.Core.Notifications.Discord
             _proxy.SendPayload(payload, Settings);
         }
 
-        public override void OnRename(Author author)
+        public override void OnRename(Author author, List<RenamedBookFile> renamedFiles)
         {
             var attachments = new List<Embed>
                               {
diff --git a/src/NzbDrone.Core/Notifications/GrabMessage.cs b/src/NzbDrone.Core/Notifications/GrabMessage.cs
index 16f738b90..fce697c70 100644
--- a/src/NzbDrone.Core/Notifications/GrabMessage.cs
+++ b/src/NzbDrone.Core/Notifications/GrabMessage.cs
@@ -8,9 +8,10 @@ namespace NzbDrone.Core.Notifications
     {
         public string Message { get; set; }
         public Author Author { get; set; }
-        public RemoteBook Book { get; set; }
+        public RemoteBook RemoteBook { get; set; }
         public QualityModel Quality { get; set; }
-        public string DownloadClient { get; set; }
+        public string DownloadClientType { get; set; }
+        public string DownloadClientName { get; set; }
         public string DownloadId { get; set; }
 
         public override string ToString()
diff --git a/src/NzbDrone.Core/Notifications/INotification.cs b/src/NzbDrone.Core/Notifications/INotification.cs
index 6cef6ae02..f6505d98d 100644
--- a/src/NzbDrone.Core/Notifications/INotification.cs
+++ b/src/NzbDrone.Core/Notifications/INotification.cs
@@ -1,4 +1,6 @@
+using System.Collections.Generic;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.MediaFiles;
 using NzbDrone.Core.ThingiProvider;
 
 namespace NzbDrone.Core.Notifications
@@ -9,7 +11,7 @@ namespace NzbDrone.Core.Notifications
 
         void OnGrab(GrabMessage grabMessage);
         void OnReleaseImport(BookDownloadMessage message);
-        void OnRename(Author author);
+        void OnRename(Author author, List<RenamedBookFile> renamedFiles);
         void OnAuthorDelete(AuthorDeleteMessage deleteMessage);
         void OnBookDelete(BookDeleteMessage deleteMessage);
         void OnBookFileDelete(BookFileDeleteMessage deleteMessage);
diff --git a/src/NzbDrone.Core/Notifications/Notifiarr/Notifiarr.cs b/src/NzbDrone.Core/Notifications/Notifiarr/Notifiarr.cs
index 9fefdc86b..0816ab7eb 100644
--- a/src/NzbDrone.Core/Notifications/Notifiarr/Notifiarr.cs
+++ b/src/NzbDrone.Core/Notifications/Notifiarr/Notifiarr.cs
@@ -1,18 +1,18 @@
-using System;
 using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Linq;
 using FluentValidation.Results;
 using NzbDrone.Common.Extensions;
-using NzbDrone.Core.HealthCheck;
+using NzbDrone.Core.Configuration;
+using NzbDrone.Core.Notifications.Webhook;
+using NzbDrone.Core.Validation;
 
 namespace NzbDrone.Core.Notifications.Notifiarr
 {
-    public class Notifiarr : NotificationBase<NotifiarrSettings>
+    public class Notifiarr : WebhookBase<NotifiarrSettings>
     {
         private readonly INotifiarrProxy _proxy;
 
-        public Notifiarr(INotifiarrProxy proxy)
+        public Notifiarr(INotifiarrProxy proxy, IConfigFileProvider configFileProvider)
+            : base(configFileProvider)
         {
             _proxy = proxy;
         }
@@ -22,162 +22,60 @@ namespace NzbDrone.Core.Notifications.Notifiarr
 
         public override void OnGrab(GrabMessage message)
         {
-            var author = message.Author;
-            var remoteBook = message.Book;
-            var releaseGroup = remoteBook.ParsedBookInfo.ReleaseGroup;
-            var variables = new StringDictionary();
-
-            variables.Add("Readarr_EventType", "Grab");
-            variables.Add("Readarr_Author_Id", author.Id.ToString());
-            variables.Add("Readarr_Author_Name", author.Metadata.Value.Name);
-            variables.Add("Readarr_Author_GRId", author.Metadata.Value.ForeignAuthorId);
-            variables.Add("Readarr_Release_BookCount", remoteBook.Books.Count.ToString());
-            variables.Add("Readarr_Release_BookReleaseDates", string.Join(",", remoteBook.Books.Select(e => e.ReleaseDate)));
-            variables.Add("Readarr_Release_BookTitles", string.Join("|", remoteBook.Books.Select(e => e.Title)));
-            variables.Add("Readarr_Release_BookIds", string.Join("|", remoteBook.Books.Select(e => e.Id.ToString())));
-            variables.Add("Readarr_Release_GRIds", remoteBook.Books.Select(x => x.Editions.Value.Single(e => e.Monitored).ForeignEditionId).ConcatToString("|"));
-            variables.Add("Readarr_Release_Title", remoteBook.Release.Title);
-            variables.Add("Readarr_Release_Indexer", remoteBook.Release.Indexer ?? string.Empty);
-            variables.Add("Readarr_Release_Size", remoteBook.Release.Size.ToString());
-            variables.Add("Readarr_Release_Quality", remoteBook.ParsedBookInfo.Quality.Quality.Name);
-            variables.Add("Readarr_Release_QualityVersion", remoteBook.ParsedBookInfo.Quality.Revision.Version.ToString());
-            variables.Add("Readarr_Release_ReleaseGroup", releaseGroup ?? string.Empty);
-            variables.Add("Readarr_Download_Client", message.DownloadClient ?? string.Empty);
-            variables.Add("Readarr_Download_Id", message.DownloadId ?? string.Empty);
-
-            _proxy.SendNotification(variables, Settings);
+            _proxy.SendNotification(BuildOnGrabPayload(message), Settings);
         }
 
         public override void OnReleaseImport(BookDownloadMessage message)
         {
-            var author = message.Author;
-            var book = message.Book;
-            var variables = new StringDictionary();
-
-            variables.Add("Readarr_EventType", "Download");
-            variables.Add("Readarr_Author_Id", author.Id.ToString());
-            variables.Add("Readarr_Author_Name", author.Metadata.Value.Name);
-            variables.Add("Readarr_Author_Path", author.Path);
-            variables.Add("Readarr_Author_GRId", author.Metadata.Value.ForeignAuthorId);
-            variables.Add("Readarr_Book_Id", book.Id.ToString());
-            variables.Add("Readarr_Book_Title", book.Title);
-            variables.Add("Readarr_Book_GRId", book.Editions.Value.Single(e => e.Monitored).ForeignEditionId.ToString());
-            variables.Add("Readarr_Book_ReleaseDate", book.ReleaseDate.ToString());
-            variables.Add("Readarr_Download_Client", message.DownloadClient ?? string.Empty);
-            variables.Add("Readarr_Download_Id", message.DownloadId ?? string.Empty);
-
-            if (message.BookFiles.Any())
-            {
-                variables.Add("Readarr_AddedBookPaths", string.Join("|", message.BookFiles.Select(e => e.Path)));
-            }
-
-            if (message.OldFiles.Any())
-            {
-                variables.Add("Readarr_DeletedPaths", string.Join("|", message.OldFiles.Select(e => e.Path)));
-            }
-
-            _proxy.SendNotification(variables, Settings);
+            _proxy.SendNotification(BuildOnReleaseImportPayload(message), Settings);
         }
 
         public override void OnAuthorDelete(AuthorDeleteMessage deleteMessage)
         {
-            var author = deleteMessage.Author;
-            var variables = new StringDictionary();
-
-            variables.Add("Readarr_EventType", "AuthorDelete");
-            variables.Add("Readarr_Author_Id", author.Id.ToString());
-            variables.Add("Readarr_Author_Name", author.Name);
-            variables.Add("Readarr_Author_Path", author.Path);
-            variables.Add("Readarr_Author_GoodreadsId", author.ForeignAuthorId);
-            variables.Add("Readarr_Author_DeletedFiles", deleteMessage.DeletedFiles.ToString());
-
-            _proxy.SendNotification(variables, Settings);
+            _proxy.SendNotification(BuildOnAuthorDelete(deleteMessage), Settings);
         }
 
         public override void OnBookDelete(BookDeleteMessage deleteMessage)
         {
-            var author = deleteMessage.Book.Author.Value;
-            var book = deleteMessage.Book;
-
-            var variables = new StringDictionary();
-
-            variables.Add("Readarr_EventType", "BookDelete");
-            variables.Add("Readarr_Author_Id", author.Id.ToString());
-            variables.Add("Readarr_Author_Name", author.Name);
-            variables.Add("Readarr_Author_Path", author.Path);
-            variables.Add("Readarr_Author_GoodreadsId", author.ForeignAuthorId);
-            variables.Add("Readarr_Book_Id", book.Id.ToString());
-            variables.Add("Readarr_Book_Title", book.Title);
-            variables.Add("Readarr_Book_GoodreadsId", book.ForeignBookId);
-            variables.Add("Readarr_Book_DeletedFiles", deleteMessage.DeletedFiles.ToString());
-
-            _proxy.SendNotification(variables, Settings);
+            _proxy.SendNotification(BuildOnBookDelete(deleteMessage), Settings);
         }
 
         public override void OnBookFileDelete(BookFileDeleteMessage deleteMessage)
         {
-            var author = deleteMessage.Book.Author.Value;
-            var book = deleteMessage.Book;
-            var bookFile = deleteMessage.BookFile;
-            var edition = bookFile.Edition.Value;
-
-            var variables = new StringDictionary();
-
-            variables.Add("Readarr_EventType", "BookFileDelete");
-            variables.Add("Readarr_Delete_Reason", deleteMessage.Reason.ToString());
-            variables.Add("Readarr_Author_Id", author.Id.ToString());
-            variables.Add("Readarr_Author_Name", author.Name);
-            variables.Add("Readarr_Author_GoodreadsId", author.ForeignAuthorId);
-            variables.Add("Readarr_Book_Id", book.Id.ToString());
-            variables.Add("Readarr_Book_Title", book.Title);
-            variables.Add("Readarr_Book_GoodreadsId", book.ForeignBookId);
-            variables.Add("Readarr_BookFile_Id", bookFile.Id.ToString());
-            variables.Add("Readarr_BookFile_Path", bookFile.Path);
-            variables.Add("Readarr_BookFile_Quality", bookFile.Quality.Quality.Name);
-            variables.Add("Readarr_BookFile_QualityVersion", bookFile.Quality.Revision.Version.ToString());
-            variables.Add("Readarr_BookFile_ReleaseGroup", bookFile.ReleaseGroup ?? string.Empty);
-            variables.Add("Readarr_BookFile_SceneName", bookFile.SceneName ?? string.Empty);
-            variables.Add("Readarr_BookFile_Edition_Id", edition.Id.ToString());
-            variables.Add("Readarr_BookFile_Edition_Name", edition.Title);
-            variables.Add("Readarr_BookFile_Edition_GoodreadsId", edition.ForeignEditionId);
-            variables.Add("Readarr_BookFile_Edition_Isbn13", edition.Isbn13);
-            variables.Add("Readarr_BookFile_Edition_Asin", edition.Asin);
-
-            _proxy.SendNotification(variables, Settings);
+            _proxy.SendNotification(BuildOnBookFileDelete(deleteMessage), Settings);
         }
 
         public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
         {
-            var variables = new StringDictionary();
-
-            variables.Add("Readarr_EventType", "HealthIssue");
-            variables.Add("Readarr_Health_Issue_Level", Enum.GetName(typeof(HealthCheckResult), healthCheck.Type));
-            variables.Add("Readarr_Health_Issue_Message", healthCheck.Message);
-            variables.Add("Readarr_Health_Issue_Type", healthCheck.Source.Name);
-            variables.Add("Readarr_Health_Issue_Wiki", healthCheck.WikiUrl.ToString() ?? string.Empty);
-
-            _proxy.SendNotification(variables, Settings);
+            _proxy.SendNotification(BuildHealthPayload(healthCheck), Settings);
         }
 
         public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
         {
-            var variables = new StringDictionary();
-
-            variables.Add("Readarr_EventType", "ApplicationUpdate");
-            variables.Add("Readarr_Update_Message", updateMessage.Message);
-            variables.Add("Readarr_Update_NewVersion", updateMessage.NewVersion.ToString());
-            variables.Add("Readarr_Update_PreviousVersion", updateMessage.PreviousVersion.ToString());
-
-            _proxy.SendNotification(variables, Settings);
+            _proxy.SendNotification(BuildApplicationUpdatePayload(updateMessage), Settings);
         }
 
         public override ValidationResult Test()
         {
             var failures = new List<ValidationFailure>();
 
-            failures.AddIfNotNull(_proxy.Test(Settings));
+            failures.AddIfNotNull(SendWebhookTest());
 
             return new ValidationResult(failures);
         }
+
+        private ValidationFailure SendWebhookTest()
+        {
+            try
+            {
+                _proxy.SendNotification(BuildTestPayload(), Settings);
+            }
+            catch (NotifiarrException ex)
+            {
+                return new NzbDroneValidationFailure("APIKey", ex.Message);
+            }
+
+            return null;
+        }
     }
 }
diff --git a/src/NzbDrone.Core/Notifications/Notifiarr/NotifiarrProxy.cs b/src/NzbDrone.Core/Notifications/Notifiarr/NotifiarrProxy.cs
index 6038dc8bf..704ffee7e 100644
--- a/src/NzbDrone.Core/Notifications/Notifiarr/NotifiarrProxy.cs
+++ b/src/NzbDrone.Core/Notifications/Notifiarr/NotifiarrProxy.cs
@@ -1,98 +1,68 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Net;
-using FluentValidation.Results;
-using NLog;
-using NzbDrone.Common.EnvironmentInfo;
+using System.Net.Http;
 using NzbDrone.Common.Http;
+using NzbDrone.Common.Serializer;
+using NzbDrone.Core.Notifications.Webhook;
 
 namespace NzbDrone.Core.Notifications.Notifiarr
 {
     public interface INotifiarrProxy
     {
-        void SendNotification(StringDictionary message, NotifiarrSettings settings);
-        ValidationFailure Test(NotifiarrSettings settings);
+        void SendNotification(WebhookPayload payload, NotifiarrSettings settings);
     }
 
     public class NotifiarrProxy : INotifiarrProxy
     {
-        private const string URL = "https://notifiarr.com/notifier.php";
+        private const string URL = "https://notifiarr.com";
         private readonly IHttpClient _httpClient;
-        private readonly Logger _logger;
 
-        public NotifiarrProxy(IHttpClient httpClient, Logger logger)
+        public NotifiarrProxy(IHttpClient httpClient)
         {
             _httpClient = httpClient;
-            _logger = logger;
         }
 
-        public void SendNotification(StringDictionary message, NotifiarrSettings settings)
+        public void SendNotification(WebhookPayload payload, NotifiarrSettings settings)
         {
-            try
-            {
-                ProcessNotification(message, settings);
-            }
-            catch (NotifiarrException ex)
-            {
-                _logger.Error(ex, "Unable to send notification");
-                throw new NotifiarrException("Unable to send notification");
-            }
+            ProcessNotification(payload, settings);
         }
 
-        public ValidationFailure Test(NotifiarrSettings settings)
+        private void ProcessNotification(WebhookPayload payload, NotifiarrSettings settings)
         {
             try
             {
-                var variables = new StringDictionary();
-                variables.Add("Readarr_EventType", "Test");
+                var request = new HttpRequestBuilder(URL + "/api/v1/notification/readarr")
+                    .Accept(HttpAccept.Json)
+                    .SetHeader("X-API-Key", settings.APIKey)
+                    .Build();
 
-                SendNotification(variables, settings);
-                return null;
-            }
-            catch (HttpException ex)
-            {
-                if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
-                {
-                    _logger.Error(ex, "API key is invalid: " + ex.Message);
-                    return new ValidationFailure("APIKey", "API key is invalid");
-                }
+                request.Method = HttpMethod.Post;
 
-                _logger.Error(ex, "Unable to send test message: " + ex.Message);
-                return new ValidationFailure("APIKey", "Unable to send test notification");
-            }
-            catch (Exception ex)
-            {
-                _logger.Error(ex, "Unable to send test notification: " + ex.Message);
-                return new ValidationFailure("", "Unable to send test notification");
-            }
-        }
-
-        private void ProcessNotification(StringDictionary message, NotifiarrSettings settings)
-        {
-            try
-            {
-                var requestBuilder = new HttpRequestBuilder(URL).Post();
-                requestBuilder.AddFormParameter("api", settings.APIKey).Build();
-
-                foreach (string key in message.Keys)
-                {
-                    requestBuilder.AddFormParameter(key, message[key]);
-                }
-
-                var request = requestBuilder.Build();
+                request.Headers.ContentType = "application/json";
+                request.SetContent(payload.ToJson());
 
                 _httpClient.Post(request);
             }
             catch (HttpException ex)
             {
-                if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
+                var responseCode = ex.Response.StatusCode;
+                switch ((int)responseCode)
                 {
-                    _logger.Error(ex, "API key is invalid");
-                    throw;
+                    case 401:
+                        throw new NotifiarrException("API key is invalid");
+                    case 400:
+                        throw new NotifiarrException("Unable to send notification. Ensure Readarr Integration is enabled & assigned a channel on Notifiarr");
+                    case 502:
+                    case 503:
+                    case 504:
+                        throw new NotifiarrException("Unable to send notification. Service Unavailable", ex);
+                    case 520:
+                    case 521:
+                    case 522:
+                    case 523:
+                    case 524:
+                        throw new NotifiarrException("Cloudflare Related HTTP Error - Unable to send notification", ex);
+                    default:
+                        throw new NotifiarrException("Unknown HTTP Error - Unable to send notification", ex);
                 }
-
-                throw new NotifiarrException("Unable to send notification", ex);
             }
         }
     }
diff --git a/src/NzbDrone.Core/Notifications/NotificationBase.cs b/src/NzbDrone.Core/Notifications/NotificationBase.cs
index cb733bdf2..ce8c57613 100644
--- a/src/NzbDrone.Core/Notifications/NotificationBase.cs
+++ b/src/NzbDrone.Core/Notifications/NotificationBase.cs
@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using FluentValidation.Results;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.MediaFiles;
 using NzbDrone.Core.ThingiProvider;
 
 namespace NzbDrone.Core.Notifications
@@ -52,7 +53,7 @@ namespace NzbDrone.Core.Notifications
         {
         }
 
-        public virtual void OnRename(Author author)
+        public virtual void OnRename(Author author, List<RenamedBookFile> renamedFiles)
         {
         }
 
diff --git a/src/NzbDrone.Core/Notifications/NotificationService.cs b/src/NzbDrone.Core/Notifications/NotificationService.cs
index d4a1f6ceb..a16a7e738 100644
--- a/src/NzbDrone.Core/Notifications/NotificationService.cs
+++ b/src/NzbDrone.Core/Notifications/NotificationService.cs
@@ -124,8 +124,9 @@ namespace NzbDrone.Core.Notifications
                 Message = GetMessage(message.Book.Author, message.Book.Books, message.Book.ParsedBookInfo.Quality),
                 Author = message.Book.Author,
                 Quality = message.Book.ParsedBookInfo.Quality,
-                Book = message.Book,
-                DownloadClient = message.DownloadClient,
+                RemoteBook = message.Book,
+                DownloadClientName = message.DownloadClientName,
+                DownloadClientType = message.DownloadClient,
                 DownloadId = message.DownloadId
             };
 
@@ -159,7 +160,7 @@ namespace NzbDrone.Core.Notifications
                 Message = GetBookDownloadMessage(message.Author, message.Book, message.ImportedBooks),
                 Author = message.Author,
                 Book = message.Book,
-                DownloadClient = message.DownloadClientInfo?.Name,
+                DownloadClientInfo = message.DownloadClientInfo,
                 DownloadId = message.DownloadId,
                 BookFiles = message.ImportedBooks,
                 OldFiles = message.OldFiles,
@@ -192,7 +193,7 @@ namespace NzbDrone.Core.Notifications
                 {
                     if (ShouldHandleAuthor(notification.Definition, message.Author))
                     {
-                        notification.OnRename(message.Author);
+                        notification.OnRename(message.Author, message.RenamedFiles);
                     }
                 }
                 catch (Exception ex)
diff --git a/src/NzbDrone.Core/Notifications/Slack/Slack.cs b/src/NzbDrone.Core/Notifications/Slack/Slack.cs
index 126f021aa..0e73d0a4a 100644
--- a/src/NzbDrone.Core/Notifications/Slack/Slack.cs
+++ b/src/NzbDrone.Core/Notifications/Slack/Slack.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using FluentValidation.Results;
 using NzbDrone.Common.Extensions;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.MediaFiles;
 using NzbDrone.Core.Notifications.Slack.Payloads;
 using NzbDrone.Core.Validation;
 
@@ -54,7 +55,7 @@ namespace NzbDrone.Core.Notifications.Slack
             _proxy.SendPayload(payload, Settings);
         }
 
-        public override void OnRename(Author author)
+        public override void OnRename(Author author, List<RenamedBookFile> renamedFiles)
         {
             var attachments = new List<Attachment>
                               {
diff --git a/src/NzbDrone.Core/Notifications/Subsonic/Subsonic.cs b/src/NzbDrone.Core/Notifications/Subsonic/Subsonic.cs
index fc92ab20b..bebf424de 100644
--- a/src/NzbDrone.Core/Notifications/Subsonic/Subsonic.cs
+++ b/src/NzbDrone.Core/Notifications/Subsonic/Subsonic.cs
@@ -4,6 +4,7 @@ using FluentValidation.Results;
 using NLog;
 using NzbDrone.Common.Extensions;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.MediaFiles;
 
 namespace NzbDrone.Core.Notifications.Subsonic
 {
@@ -35,7 +36,7 @@ namespace NzbDrone.Core.Notifications.Subsonic
             Update();
         }
 
-        public override void OnRename(Author author)
+        public override void OnRename(Author author, List<RenamedBookFile> renamedFiles)
         {
             Update();
         }
diff --git a/src/NzbDrone.Core/Notifications/Synology/SynologyIndexer.cs b/src/NzbDrone.Core/Notifications/Synology/SynologyIndexer.cs
index bd58073e1..94bd62400 100644
--- a/src/NzbDrone.Core/Notifications/Synology/SynologyIndexer.cs
+++ b/src/NzbDrone.Core/Notifications/Synology/SynologyIndexer.cs
@@ -3,6 +3,7 @@ using FluentValidation.Results;
 using NzbDrone.Common.EnvironmentInfo;
 using NzbDrone.Common.Extensions;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.MediaFiles;
 
 namespace NzbDrone.Core.Notifications.Synology
 {
@@ -38,7 +39,7 @@ namespace NzbDrone.Core.Notifications.Synology
             }
         }
 
-        public override void OnRename(Author author)
+        public override void OnRename(Author author, List<RenamedBookFile> renamedFiles)
         {
             if (Settings.UpdateLibrary)
             {
diff --git a/src/NzbDrone.Core/Notifications/Webhook/Webhook.cs b/src/NzbDrone.Core/Notifications/Webhook/Webhook.cs
index aef9671b4..04b5b8e34 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/Webhook.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/Webhook.cs
@@ -3,147 +3,67 @@ using System.Linq;
 using FluentValidation.Results;
 using NzbDrone.Common.Extensions;
 using NzbDrone.Core.Books;
+using NzbDrone.Core.Configuration;
+using NzbDrone.Core.MediaFiles;
 using NzbDrone.Core.Validation;
 
 namespace NzbDrone.Core.Notifications.Webhook
 {
-    public class Webhook : NotificationBase<WebhookSettings>
+    public class Webhook : WebhookBase<WebhookSettings>
     {
         private readonly IWebhookProxy _proxy;
 
-        public Webhook(IWebhookProxy proxy)
+        public Webhook(IWebhookProxy proxy, IConfigFileProvider configFileProvider)
+            : base(configFileProvider)
         {
             _proxy = proxy;
         }
 
-        public override string Link => "https://wiki.servarr.com/readarr/settings#connect";
+        public override string Link => "https://wiki.servarr.com/readarr/settings#connections";
 
         public override void OnGrab(GrabMessage message)
         {
-            var remoteBook = message.Book;
-            var quality = message.Quality;
-
-            var payload = new WebhookGrabPayload
-            {
-                EventType = WebhookEventType.Grab,
-                Author = new WebhookAuthor(message.Author),
-                Books = remoteBook.Books.ConvertAll(x => new WebhookBook(x)
-                {
-                    // TODO: Stop passing these parameters inside an book v3
-                    Quality = quality.Quality.Name,
-                    QualityVersion = quality.Revision.Version,
-                    ReleaseGroup = remoteBook.ParsedBookInfo.ReleaseGroup
-                }),
-                Release = new WebhookRelease(quality, remoteBook),
-                DownloadClient = message.DownloadClient,
-                DownloadId = message.DownloadId
-            };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildOnGrabPayload(message), Settings);
         }
 
         public override void OnReleaseImport(BookDownloadMessage message)
         {
-            var bookFiles = message.BookFiles;
-
-            var payload = new WebhookImportPayload
-            {
-                EventType = WebhookEventType.Download,
-                Author = new WebhookAuthor(message.Author),
-                Book = new WebhookBook(message.Book),
-                BookFiles = bookFiles.ConvertAll(x => new WebhookBookFile(x)),
-                IsUpgrade = message.OldFiles.Any(),
-                DownloadClient = message.DownloadClient,
-                DownloadId = message.DownloadId
-            };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildOnReleaseImportPayload(message), Settings);
         }
 
-        public override void OnRename(Author author)
+        public override void OnRename(Author author, List<RenamedBookFile> renamedFiles)
         {
-            var payload = new WebhookRenamePayload
-            {
-                EventType = WebhookEventType.Rename,
-                Author = new WebhookAuthor(author)
-            };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildOnRenamePayload(author, renamedFiles), Settings);
         }
 
         public override void OnAuthorDelete(AuthorDeleteMessage deleteMessage)
         {
-            var payload = new WebhookAuthorDeletePayload
-            {
-                EventType = WebhookEventType.Delete,
-                Author = new WebhookAuthor(deleteMessage.Author),
-                DeletedFiles = deleteMessage.DeletedFiles
-            };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildOnAuthorDelete(deleteMessage), Settings);
         }
 
         public override void OnBookDelete(BookDeleteMessage deleteMessage)
         {
-            var payload = new WebhookBookDeletePayload
-            {
-                EventType = WebhookEventType.Delete,
-                Author = new WebhookAuthor(deleteMessage.Book.Author),
-                Book = new WebhookBook(deleteMessage.Book)
-            };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildOnBookDelete(deleteMessage), Settings);
         }
 
         public override void OnBookFileDelete(BookFileDeleteMessage deleteMessage)
         {
-            var payload = new WebhookBookFileDeletePayload
-            {
-                EventType = WebhookEventType.Delete,
-                Author = new WebhookAuthor(deleteMessage.Book.Author),
-                Book = new WebhookBook(deleteMessage.Book),
-                BookFile = new WebhookBookFile(deleteMessage.BookFile)
-            };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildOnBookFileDelete(deleteMessage), Settings);
         }
 
         public override void OnBookRetag(BookRetagMessage message)
         {
-            var payload = new WebhookRetagPayload
-            {
-                EventType = WebhookEventType.Retag,
-                Author = new WebhookAuthor(message.Author)
-            };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildOnBookRetagPayload(message), Settings);
         }
 
         public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
         {
-            var payload = new WebhookHealthPayload
-                          {
-                              EventType = WebhookEventType.Health,
-                              Level = healthCheck.Type,
-                              Message = healthCheck.Message,
-                              Type = healthCheck.Source.Name,
-                              WikiUrl = healthCheck.WikiUrl?.ToString()
-                          };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildHealthPayload(healthCheck), Settings);
         }
 
         public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
         {
-            var payload = new WebhookApplicationUpdatePayload
-            {
-                EventType = WebhookEventType.ApplicationUpdate,
-                Message = updateMessage.Message,
-                PreviousVersion = updateMessage.PreviousVersion.ToString(),
-                NewVersion = updateMessage.NewVersion.ToString()
-            };
-
-            _proxy.SendWebhook(payload, Settings);
+            _proxy.SendWebhook(BuildApplicationUpdatePayload(updateMessage), Settings);
         }
 
         public override string Name => "Webhook";
@@ -161,27 +81,7 @@ namespace NzbDrone.Core.Notifications.Webhook
         {
             try
             {
-                var payload = new WebhookGrabPayload
-                {
-                    EventType = WebhookEventType.Test,
-                    Author = new WebhookAuthor()
-                    {
-                        Id = 1,
-                        Name = "Test Name",
-                        Path = "C:\\testpath",
-                        MBId = "aaaaa-aaa-aaaa-aaaaaa"
-                    },
-                    Books = new List<WebhookBook>()
-                    {
-                            new WebhookBook()
-                            {
-                                Id = 123,
-                                Title = "Test title"
-                            }
-                    }
-                };
-
-                _proxy.SendWebhook(payload, Settings);
+                _proxy.SendWebhook(BuildTestPayload(), Settings);
             }
             catch (WebhookException ex)
             {
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookApplicationUpdatePayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookApplicationUpdatePayload.cs
index e05be69bc..66a6ff382 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/WebhookApplicationUpdatePayload.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookApplicationUpdatePayload.cs
@@ -1,5 +1,3 @@
-using NzbDrone.Core.HealthCheck;
-
 namespace NzbDrone.Core.Notifications.Webhook
 {
     public class WebhookApplicationUpdatePayload : WebhookPayload
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookAuthor.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookAuthor.cs
index f099f8943..1f84c8ebf 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/WebhookAuthor.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookAuthor.cs
@@ -7,7 +7,7 @@ namespace NzbDrone.Core.Notifications.Webhook
         public int Id { get; set; }
         public string Name { get; set; }
         public string Path { get; set; }
-        public string MBId { get; set; }
+        public string GoodreadsId { get; set; }
 
         public WebhookAuthor()
         {
@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Notifications.Webhook
             Id = author.Id;
             Name = author.Name;
             Path = author.Path;
-            MBId = author.Metadata.Value.ForeignAuthorId;
+            GoodreadsId = author.Metadata.Value.ForeignAuthorId;
         }
     }
 }
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs
new file mode 100644
index 000000000..369fbe3b0
--- /dev/null
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs
@@ -0,0 +1,176 @@
+using System.Collections.Generic;
+using System.Linq;
+using NzbDrone.Core.Books;
+using NzbDrone.Core.Configuration;
+using NzbDrone.Core.MediaFiles;
+using NzbDrone.Core.ThingiProvider;
+
+namespace NzbDrone.Core.Notifications.Webhook
+{
+    public abstract class WebhookBase<TSettings> : NotificationBase<TSettings>
+        where TSettings : IProviderConfig, new()
+    {
+        private readonly IConfigFileProvider _configFileProvider;
+
+        protected WebhookBase(IConfigFileProvider configFileProvider)
+            : base()
+        {
+            _configFileProvider = configFileProvider;
+        }
+
+        public WebhookGrabPayload BuildOnGrabPayload(GrabMessage message)
+        {
+            var remoteBook = message.RemoteBook;
+            var quality = message.Quality;
+
+            return new WebhookGrabPayload
+            {
+                EventType = WebhookEventType.Grab,
+                InstanceName = _configFileProvider.InstanceName,
+                Author = new WebhookAuthor(message.Author),
+                Books = remoteBook.Books.ConvertAll(x => new WebhookBook(x)
+                {
+                    // TODO: Stop passing these parameters inside an book v3
+                    Quality = quality.Quality.Name,
+                    QualityVersion = quality.Revision.Version,
+                    ReleaseGroup = remoteBook.ParsedBookInfo.ReleaseGroup
+                }),
+                Release = new WebhookRelease(quality, remoteBook),
+                DownloadClient = message.DownloadClientName,
+                DownloadClientType = message.DownloadClientType,
+                DownloadId = message.DownloadId
+            };
+        }
+
+        public WebhookImportPayload BuildOnReleaseImportPayload(BookDownloadMessage message)
+        {
+            var trackFiles = message.BookFiles;
+
+            var payload = new WebhookImportPayload
+            {
+                EventType = WebhookEventType.Download,
+                InstanceName = _configFileProvider.InstanceName,
+                Author = new WebhookAuthor(message.Author),
+                Book = new WebhookBook(message.Book),
+                BookFiles = trackFiles.ConvertAll(x => new WebhookBookFile(x)),
+                IsUpgrade = message.OldFiles.Any(),
+                DownloadClient = message.DownloadClientInfo?.Name,
+                DownloadClientType = message.DownloadClientInfo?.Type,
+                DownloadId = message.DownloadId
+            };
+
+            if (message.OldFiles.Any())
+            {
+                payload.DeletedFiles = message.OldFiles.ConvertAll(x => new WebhookBookFile(x));
+            }
+
+            return payload;
+        }
+
+        public WebhookRenamePayload BuildOnRenamePayload(Author author, List<RenamedBookFile> renamedFiles)
+        {
+            return new WebhookRenamePayload
+            {
+                EventType = WebhookEventType.Rename,
+                InstanceName = _configFileProvider.InstanceName,
+                Author = new WebhookAuthor(author),
+                RenamedBookFiles = renamedFiles.ConvertAll(x => new WebhookRenamedBookFile(x))
+            };
+        }
+
+        public WebhookRetagPayload BuildOnBookRetagPayload(BookRetagMessage message)
+        {
+            return new WebhookRetagPayload
+            {
+                EventType = WebhookEventType.Retag,
+                InstanceName = _configFileProvider.InstanceName,
+                Author = new WebhookAuthor(message.Author),
+                BookFile = new WebhookBookFile(message.BookFile)
+            };
+        }
+
+        public WebhookBookDeletePayload BuildOnBookDelete(BookDeleteMessage deleteMessage)
+        {
+            return new WebhookBookDeletePayload
+            {
+                EventType = WebhookEventType.Delete,
+                InstanceName = _configFileProvider.InstanceName,
+                Author = new WebhookAuthor(deleteMessage.Book.Author),
+                Book = new WebhookBook(deleteMessage.Book),
+                DeletedFiles = deleteMessage.DeletedFiles
+            };
+        }
+
+        public WebhookBookFileDeletePayload BuildOnBookFileDelete(BookFileDeleteMessage deleteMessage)
+        {
+            return new WebhookBookFileDeletePayload
+            {
+                EventType = WebhookEventType.Delete,
+                InstanceName = _configFileProvider.InstanceName,
+                Author = new WebhookAuthor(deleteMessage.Book.Author),
+                Book = new WebhookBook(deleteMessage.Book),
+                BookFile = new WebhookBookFile(deleteMessage.BookFile)
+            };
+        }
+
+        public WebhookAuthorDeletePayload BuildOnAuthorDelete(AuthorDeleteMessage deleteMessage)
+        {
+            return new WebhookAuthorDeletePayload
+            {
+                EventType = WebhookEventType.Delete,
+                InstanceName = _configFileProvider.InstanceName,
+                Author = new WebhookAuthor(deleteMessage.Author),
+                DeletedFiles = deleteMessage.DeletedFiles
+            };
+        }
+
+        protected WebhookHealthPayload BuildHealthPayload(HealthCheck.HealthCheck healthCheck)
+        {
+            return new WebhookHealthPayload
+            {
+                EventType = WebhookEventType.Health,
+                InstanceName = _configFileProvider.InstanceName,
+                Level = healthCheck.Type,
+                Message = healthCheck.Message,
+                Type = healthCheck.Source.Name,
+                WikiUrl = healthCheck.WikiUrl?.ToString()
+            };
+        }
+
+        protected WebhookApplicationUpdatePayload BuildApplicationUpdatePayload(ApplicationUpdateMessage updateMessage)
+        {
+            return new WebhookApplicationUpdatePayload
+            {
+                EventType = WebhookEventType.ApplicationUpdate,
+                InstanceName = _configFileProvider.InstanceName,
+                Message = updateMessage.Message,
+                PreviousVersion = updateMessage.PreviousVersion.ToString(),
+                NewVersion = updateMessage.NewVersion.ToString()
+            };
+        }
+
+        protected WebhookPayload BuildTestPayload()
+        {
+            return new WebhookGrabPayload
+            {
+                EventType = WebhookEventType.Test,
+                InstanceName = _configFileProvider.InstanceName,
+                Author = new WebhookAuthor()
+                {
+                    Id = 1,
+                    Name = "Test Name",
+                    Path = "C:\\testpath",
+                    GoodreadsId = "aaaaa-aaa-aaaa-aaaaaa"
+                },
+                Books = new List<WebhookBook>()
+                    {
+                            new WebhookBook()
+                            {
+                                Id = 123,
+                                Title = "Test title"
+                            }
+                    }
+            };
+        }
+    }
+}
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookBookDeletePayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookBookDeletePayload.cs
index 804ddf77b..3f0d6b1ef 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/WebhookBookDeletePayload.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookBookDeletePayload.cs
@@ -4,5 +4,6 @@ namespace NzbDrone.Core.Notifications.Webhook
     {
         public WebhookAuthor Author { get; set; }
         public WebhookBook Book { get; set; }
+        public bool DeletedFiles { get; set; }
     }
 }
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabPayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabPayload.cs
index 5990afce7..f6bc7b1df 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabPayload.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabPayload.cs
@@ -8,6 +8,7 @@ namespace NzbDrone.Core.Notifications.Webhook
         public List<WebhookBook> Books { get; set; }
         public WebhookRelease Release { get; set; }
         public string DownloadClient { get; set; }
+        public string DownloadClientType { get; set; }
         public string DownloadId { get; set; }
     }
 }
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs
index aaf779e73..cc8cdeb13 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs
@@ -7,8 +7,10 @@ namespace NzbDrone.Core.Notifications.Webhook
         public WebhookAuthor Author { get; set; }
         public WebhookBook Book { get; set; }
         public List<WebhookBookFile> BookFiles { get; set; }
+        public List<WebhookBookFile> DeletedFiles { get; set; }
         public bool IsUpgrade { get; set; }
         public string DownloadClient { get; set; }
+        public string DownloadClientType { get; set; }
         public string DownloadId { get; set; }
     }
 }
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookPayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookPayload.cs
index 4b63a748e..05d51c7c1 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/WebhookPayload.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookPayload.cs
@@ -3,5 +3,6 @@ namespace NzbDrone.Core.Notifications.Webhook
     public class WebhookPayload
     {
         public WebhookEventType EventType { get; set; }
+        public string InstanceName { get; set; }
     }
 }
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookRenamePayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookRenamePayload.cs
index bb9a3e56b..d2a7bf7b7 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/WebhookRenamePayload.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookRenamePayload.cs
@@ -1,7 +1,10 @@
+using System.Collections.Generic;
+
 namespace NzbDrone.Core.Notifications.Webhook
 {
     public class WebhookRenamePayload : WebhookPayload
     {
         public WebhookAuthor Author { get; set; }
+        public List<WebhookRenamedBookFile> RenamedBookFiles { get; set; }
     }
 }
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookRenamedBookFile.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookRenamedBookFile.cs
new file mode 100644
index 000000000..4dbeddec2
--- /dev/null
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookRenamedBookFile.cs
@@ -0,0 +1,15 @@
+using NzbDrone.Core.MediaFiles;
+
+namespace NzbDrone.Core.Notifications.Webhook
+{
+    public class WebhookRenamedBookFile : WebhookBookFile
+    {
+        public WebhookRenamedBookFile(RenamedBookFile renamedMovie)
+            : base(renamedMovie.BookFile)
+        {
+            PreviousPath = renamedMovie.PreviousPath;
+        }
+
+        public string PreviousPath { get; set; }
+    }
+}
diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookRetagPayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookRetagPayload.cs
index 67e3ec729..877097dc6 100644
--- a/src/NzbDrone.Core/Notifications/Webhook/WebhookRetagPayload.cs
+++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookRetagPayload.cs
@@ -3,5 +3,6 @@ namespace NzbDrone.Core.Notifications.Webhook
     public class WebhookRetagPayload : WebhookPayload
     {
         public WebhookAuthor Author { get; set; }
+        public WebhookBookFile BookFile { get; set; }
     }
 }